iosobjective-c

How does the compiler determine which messages can be sent to an id type variable?


I've come across this 'problem' (it's not really a problem, I merely was astonished that that was possible) while trying some things with TabBarController template in Xcode. If you use the template without storyboarding the basic setup looks like this:

- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
UIViewController *viewController1 = [[FirstViewController alloc] initWithNibName:@"FirstViewController" bundle:nil];
UIViewController *viewController2 = [[SecondViewController alloc] initWithNibName:@"SecondViewController" bundle:nil];
self.tabBarController = [[UITabBarController alloc] init];

self.tabBarController.viewControllers = @[viewController1, viewController2];

self.window.rootViewController = self.tabBarController;
[self.window makeKeyAndVisible];
return YES;
}

The viewControllers property of the tabBarController is an NSArray. Thus [self.tabBarController objectAtIndex:0] returns an id.

So I always thought if I wanted to call a method I declared at f.e. the FirstViewController class, I had to do it like this:

FirstViewController *firstVC = (FirstViewController *)[self.tabBarController objectAtIndex:0];
[firstVC someMethod];

But as it turned out, that unnecessary, the complier would also let me do the following - AS LONG AS I import a header file which declares someMethod (of course it doesn't necessarily increase readability, but anyway):

[[self.tabBarController objectAtIndex:0] someMethod];

I wasn't expecting this at all. So I'm assuming the compiler will allow calling any method on id as long as that method is declared in any class in scope of the current class (by that I mean, it's header file is imported into the current class). If the class declaring someMethod is not imported, the compiler will throw an error (But I have to add that I tested this while using ARC. It may very well be possible, that the compiler doesn't complain about calling 'unimported' methods on id when not using ARC)...

Is that assumption correct? And if possible could you provide some more information or reference about the id type?

Or did the compiler allow calling any method on id (imported or not) before ARC, and the complaint now for unimported methods is just a result of ARC?

Thx alot.


Solution

  • Or did the compiler allow calling any method on id (imported or not) before ARC, and the complaint now for unimported methods is just a result of ARC?

    This is correct. Without ARC it was a warning. With ARC, it's an error (because ARC can get into serious problems if it guesses wrong here).

    In some cases this behavior can cause some very subtle bugs with or without ARC. If there are multiple method signatures that match the selector, then the compiler may choose the wrong return type, and this can cause very surprising runtime behavior. Matt Gallagher provides a very good explanation of this in "A big weakness in Objective-C's weak typing." I've encountered the same bug he's describing, and it's something ObjC developers should be aware of even though it doesn't come up that often.