Skip to content

Instantly share code, notes, and snippets.

@jballanc
Last active August 29, 2015 14:04
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jballanc/e9556e0853e137a302c0 to your computer and use it in GitHub Desktop.
Save jballanc/e9556e0853e137a302c0 to your computer and use it in GitHub Desktop.

iOS Interview Questions

Q1.

In the UITableViewCell constructor - (id)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier what is the purpose of the reuseIdentifier? What is the advantage of setting it to a non-nil value?

Answer

The reuseIdentifier is used to group together similar rows in an UITableView. That is, rows that differ only in their content, but otherwise have similar layouts.

A UITableView will normally allocate just enough UITableViewCell objects to display the content visible in the table. If reuseIdentifier is set to a non-nil value, then when the table view is scrolled, UITableView will first attempt to reuse an already allocated UITableViewCell with the same reuseIdentifier. If you have not set reuseIdentifier then the UITableView will be forced to allocate new UITableViewCell objects for each new item that scrolls into view, potentially leading to laggy animations.

Q2.

What are different ways that you can specify the layout of elements in a UIView?

Answer

Using InterfaceBuilder, you can add a XIB file to your project, layout elements within it, and then load the XIB in your application code (either automatically, based on naming conventions, or manually). Also using InterfaceBuilder you can create a storyboard for your application.

Alternatively, using only code you can use NSLayoutConstraints to have elements in a view arranged by Auto Layout. Finally, you can create CGRects describing the exact coordinates for each element and pass them to UIView's - (id)initWithFrame:(CGRect)frame method.

Q3.

What is the difference between atomic and nonatomic properties? Which is the default for synthesized properties? When would you use one vs the other?

Answer

Properties specified as atomic are guaranteed to always return a fully initialized object. This also happens to be the default state for synthesized properties so, while it's a good practice to specify atomic to remove the potential for confusion, if you leave it off, your properties will still be atomic.

This guarantee of atomic properties comes at a cost to performance, however. If you have a property for which you know that retrieving an uninitialized value is not a risk (e.g. if all access to the property is already synchronized via other means), then setting it to nonatomic can gain you a bit of performance.

Q4.

Imagine you wanted to record the time that your application was launched, so you created a class that defined a global variable in its header: NSString *startTime;. Then, in the class's implementation you set the variable like so:

+ (void)initialize {
    NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
    [formatter setDateStyle:NSDateFormatterNoStyle];
    [formatter setTimeStyle:NSDateFormatterMediumStyle];
    startTime = [formatter stringFromDate:[NSDate date]];
}

If you then added the following line to the application:didFinishLaunchingWithOptions: method in your AppDelegate:

    NSLog(@"Application was launched at: %@", startTime);

what would you expect to be logged in the debugger console? How could you fix this to work as expected?

Answer

The debugger console will log Application was launched at: (null) because the global startTime variable has not yet been set. The initialize method of an Objective-C class is only called right before the first message is sent to that class. On the other hand, any load methods defined by Objective-C classes will be called as soon as the class is added to the Objective-C runtime. Changing initialize to load will have the desired result (but be cautious about doing too much in load, as it may increase your application's load time).

Q5.

Identify the bug in the following code:

#import "TTAppDelegate.h"

@interface TTParent : NSObject

@property (atomic) NSMutableArray *children;

@end

@implementation TTParent
@end

@interface TTChild : NSObject

@property (atomic) TTParent *parent;

@end

@implementation TTChild
@end

@implementation TTAppDelegate

- (BOOL)application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    TTParent *parent = [[TTParent alloc] init];
    parent.children = [[NSMutableArray alloc] init];
    for (int i = 0; i < 10; i++) {
        TTChild *child = [[TTChild alloc] init];
        child.parent = parent;
        [parent.children addObject:child];
    }
    return YES;
}
@end

What is the consequence of this bug? How would you fix it?

Answer

This is a classic example of a retain cycle. The parent will retain the children array, and the array will retain each TTChild object added to it. Each child object that is created will also retain its parent, so that even after the last external reference to parent is cleared, the retain count on parent will still be greater than 0 and it will not be removed.

In order to fix this, the child's reference back to the parent needs to be declared as a weak reference like so:

@interface TTChild : NSObject

@property (weak, atomic) TTParent *parent;

@end

A weak reference will not increment the target's retain count, and will be set to nil when the target is finally destroyed.

Note: For a more complicated variation on this question, you could consider two peers that keep references to each other in an array. In this case, you will need to substitute NSArray/NSMutableArray with an NSPointerArray declared as:

NSPointerArray *weakRefArray = [[NSPointerArray alloc] initWithOptions: NSPointerFunctionsWeakMemory];

since NSArray normally stores a strong reference to its members.

Q6.

Identify the bug in the following code:

@interface TTWaitController : UIViewController

@property (strong, nonatomic) UILabel *alert;

@end

@implementation TTWaitController

- (void)viewDidLoad
{
    CGRect frame = CGRectMake(20, 200, 200, 20);
    self.alert = [[UILabel alloc] initWithFrame:frame];
    self.alert.text = @"Please wait 10 seconds...";
    self.alert.textColor = [UIColor whiteColor];
    [self.view addSubview:self.alert];

    NSOperationQueue *waitQueue = [[NSOperationQueue alloc] init];
    [waitQueue addOperationWithBlock:^{
        [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
        self.alert.text = @"Thanks!";
    }];
}

@end

@implementation TTAppDelegate

- (BOOL)application:(UIApplication *)application
  didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
    self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
    self.window.rootViewController = [[TTWaitController alloc] init];
    [self.window makeKeyAndVisible];
    return YES;
}

How would you fix this issue?

Answer

When the above code dispatches work using NSOperationQueue's method addOperationWithBlock, there is no guarantee that the block being enqueued will be executed on the main thread. Notice that the content of the UILabel is being updated within the body of the block. UI updates that are not executed on the main thread can lead to undefined behavior. This code might appear to be working correctly for a long time before anything goes wrong, but UI updates should always happen on the main thread.

The easiest way to fix the potential issue is to change the body of the block so that the update is re-enqueued using the main thread's queue like so:

[waitQueue addOperationWithBlock:^{
    [NSThread sleepUntilDate:[NSDate dateWithTimeIntervalSinceNow:10]];
    [[NSOperationQueue mainQueue] addOperationWithBlock:^{
        self.alert.text = @"Thanks!";
    }];
}];
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment