快速提问。我有一个没有实现文件的类的项目。
然后在AppDelegate中我得到了:
#import "AppDelegate.h"
#import "SomeClass.h"
@interface AppDelegate ()
@property (nonatomic, strong) SomeClass *myProperty;
@end
@implementation AppDelegate
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self.myProperty hello];
// self.myProperty = [[SomeClass alloc] init]; // uncomment and fails as expected.
return YES;
}
不应该有人告诉我没有实施文件吗?某种警告或其他什么?
如果我执行alloc] init],它将无法按预期编译。
该代码实际编译。
这是github中的项目。
https://github.com/nmiyasato/noImplementation
由于
答案 0 :(得分:3)
没有。这在Objective-C的编译时或链接时是无法检测到的。
首先,编译器确切地知道 nothing 关于“头文件”或“实现文件”。 (这与新的模块系统有所改变,但这不是我们在这里讨论的内容。)
#import
不由编译器处理。它由预处理器处理。在编译器甚至看到第一行之前,它将文件SomeClass.h
作为文本splats到AppDelegate.m
。因此,所有编译器必须使用的是这一个巨大的文件,其中包含所有标题的所有文本以及此实现(现在有“整个模块优化”,这是一个链接步骤,而不是编译步骤)。它没有任何访问项目其余部分的权限。
因此编译器无法知道您没有提供实现。在ObjC中,即使编译器查看了所有代码,它实际上也无法知道在任何地方都没有实现,因为您可以在运行时添加实现。事实上,这样做非常共同。这就是所有Core Data的工作原理。实现也可以通过共享框架(这是非常常见的)链接,并且可能甚至可以在OS X上的运行时链接。或者实现可能在静态库中,因此缺少.m
仍然无益。
self.myProperty
的结果甚至可能是随机的“其他事物”,只是假装为SomeClass
。是的,我知道这听起来很疯狂。欢迎使用Core Foundation桥接的类集群。那是件好事。因此,甚至可能没有按照您的思维方式实施。 Objective-C是一种非常疯狂的动态语言。
作为一个例子,以下是合法的ObjC(它甚至有效):
@interface NSString (Hello)
- (void)hello;
@end
@implementation NSString (Hello)
- (void)hello {
NSLog(@"I'm string's Hello!");
}
@end
...
self.myProperty = (SomeClass *)@"";
[self.myProperty hello];
您可能认为链接器可能会想出来,但是当我们到达链接器时,所有对象类型都是id
,所有方法都只是选择器和方法签名。大多数类型信息都消失了。
那么,如果你拨打[[SomeClass alloc] init]
,为什么这会无法链接?首先注意它确实编译,它只是没有链接。原因是[self.myProperty hello]
是对象的消息。链接器不知道或不关心对象的类型。它只需要一个指向实例的指针。但[SomeClass alloc]
是给班级的信息。为了链接它,链接器必须有一个指向该类的指针。您会发现任何未实现的类消息都会产生链接器错误(尝试[SomeClass initialize]
)。
在您的代码中,运行时没有任何事情发生,因为self.myProperty
为零,所以没有错误。即使你有一个实现,这也是一样的。在绝大多数情况下,在链接期间将缺少实现文件,因为您系统中的某处可能会调用+alloc
。所以在实践中,这根本不应该经常出现,而且这种罕见的情况很难在不破坏大量合法ObjC的情况下被发现。