2

我在 Xcode 中尝试使用 TabBarController 模板做一些事情时遇到了这个“问题”(这不是一个真正的问题,我只是很惊讶这是可能的)。如果您使用没有故事板的模板,则基本设置如下所示:

- (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;
}

tabBarController 的 viewControllers 属性是一个 NSArray。因此[self.tabBarController objectAtIndex:0]返回一个id.

所以我一直在想,如果我想调用我在 FirstViewController 类中声明的方法,我必须这样做:

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

但事实证明,这是不必要的,编译器也会让我执行以下操作 - 只要我导入一个声明的头文件someMethod(当然它不一定会增加可读性,但无论如何):

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

我完全没想到会这样。所以我假设编译器将允许调用任何方法,id只要该方法在当前类范围内的任何类中声明(我的意思是,它的头文件被导入到当前类中)。如果someMethod未导入声明的类,编译器将抛出错误(但我必须补充一点,我在使用 ARC 时对此进行了测试。很有可能,编译器不会抱怨在id何时调用“未导入”方法不使用ARC)...

这个假设正确吗?如果可能的话,您能否提供有关 id 类型的更多信息或参考?

或者编译器是否允许在 ARC 之前调用任何方法id(是否导入),而现在对未导入方法的抱怨只是 ARC 的结果?

多谢。

4

2 回答 2

1

对象(类型id通常是从​​ 继承的任何对象的任何实例NSObject)具有方法。一个对象要么响应方法签名,要么不响应。

XCode 可能会向您发出警告,说明这不会通过类型检查响应该方法,但代码仍将运行。如果你在一个不支持它的对象上执行一个方法,你会得到一个运行时异常。您可以将任何对象转换为任何其他类型的完全不兼容的对象,并且仍然在运行时调用该对象的方法。

// This should work at runtime, but generate warning when compiled
// Don't do this, obviously, but it should "work"
NSNumber *array = [NSArray arrayWithObject:@"foobar"];
NSLog([array objectAtIndex:0]); // "foobar"

类型检查实例和在它们上调用的方法在编译时对程序员来说是一个好处,但对运行时编译的应用程序来说却没有那么多好处。

所以回答你的问题:

编译器如何确定哪些消息可以发送到对象?

它在运行时询问对象。

[myObj myMethod:123];

在 ObjC 运行时触发类似这样的伪代码:

if myObj responds to the message with signature "myMethod:"?
  send myObj the "myMethod:" message with arguments [123]
else
  throw an exception

变量声明的类型实际上在运行时根本不重要,因为变量只是一个指向对象的指针。确定该对象是否响应某个方法并不是在编译时完成的。

于 2012-10-25T21:16:54.720 回答
1

或者编译器是否允许在 ARC 之前调用 id 上的任何方法(是否导入),而现在对未导入方法的投诉只是 ARC 的结果?

这是对的。没有 ARC,这是一个警告。使用 ARC,这是一个错误(因为如果 ARC 在这里猜错了,它可能会遇到严重的问题)。

在某些情况下,这种行为可能会导致一些非常微妙的错误,无论是否使用 ARC。如果有多个方法签名与选择器匹配,那么编译器可能会选择错误的返回类型,这会导致非常令人惊讶的运行时行为。Matt Gallagher 在“Objective-C 的弱类型化的一大弱点”中对此提供了很好的解释。我遇到了他描述的同样的错误,这是 ObjC 开发人员应该注意的事情,即使它不经常出现。

于 2012-10-25T21:45:56.940 回答