Objective c 编译器如何确定哪些消息可以发送到id类型变量?

Objective c 编译器如何确定哪些消息可以发送到id类型变量?,objective-c,ios,compiler-construction,Objective C,Ios,Compiler Construction,在Xcode中尝试使用TabBarController模板时,我遇到了这个“问题”(这不是一个真正的问题,我只是惊讶于这是可能的)。如果使用不带故事板的模板,则基本设置如下所示: - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { self.window = [[UIWindow alloc] initWithFrame:[[U

在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;
}
FirstViewController *firstVC = (FirstViewController *)[self.tabBarController objectAtIndex:0];
[firstVC someMethod];
tabBarController的viewControllers属性是NSArray。因此,
[self.tabBarController objectAtIndex:0]
返回一个
id

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

- (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;
}
FirstViewController *firstVC = (FirstViewController *)[self.tabBarController objectAtIndex:0];
[firstVC someMethod];
但事实证明,这是不必要的,编译器还允许我执行以下操作-只要我导入一个声明
someMethod
的头文件(当然这不一定会增加可读性,但无论如何):

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

这个假设正确吗?如果可能的话,你能提供更多关于身份证类型的信息或参考资料吗

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

Thx很多。

对象(类型
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];
类型检查实例和对其调用的方法在编译时对程序员有好处,但在运行时对已编译的应用程序没有好处

// 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];
所以要回答你的问题:

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

它在运行时询问对象

// 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
变量声明的类型实际上在运行时根本不重要,因为变量只是指向对象的指针。计算对象是否响应某个方法不是在编译时完成的

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

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


在某些情况下,这种行为可能会导致一些非常微妙的错误,无论是否带有ARC。如果有多个方法签名与选择器匹配,那么编译器可能会选择错误的返回类型,这可能会导致非常令人惊讶的运行时行为。马特·加拉赫(Matt Gallagher)在《我遇到了他所描述的同一个bug》中对此给出了很好的解释,这是ObjC开发人员应该注意的,尽管它并不经常出现。

事实上,使用objective-c,您可以在运行时向对象添加新方法,因此编译器无论如何都不会意识到这一点。在使用
if([obj respondsToSelector:…
)调用对象之前,您应该检查对象是否支持该特定方法。好的,我明白了。但是在我的测试中,我有一个名为TestClass的类,它实现了一个名为“test”的方法。当我尝试使用
[[self.tabBarController.viewControllers objectAtIndex:0]test];
如果不导入
TestClass.h
它将不允许我构建它。(ARC语义问题)。因此,我猜这在没有ARC的情况下是可行的,但由于ARC需要了解方法实现以进行适当的内存管理,因此如果不导入
TestClass.h
,它将失败。我的信息可能有点过时,我对ARC没有做太多。但这就是它在过去的工作方式。:)这是一个非常有趣的链接!非常感谢!