快速提问。我有一个没有实现文件的类的项目。
然后在 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
谢谢
最佳答案
不,这在 Objective-C 的编译时或链接时是无法检测到的。
首先,编译器对“头文件”或“实现文件”一无所知。 (随着新的模块系统,这会发生一些变化,但这不是我们在这里讨论的内容。)#import
不由编译器处理。它由预处理器处理。它需要文件SomeClass.h
并将其放入 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]
是给类(class)的消息。为了链接它,链接器必须有一个指向该类的指针。您会发现发送给未实现类的任何消息都会产生链接器错误(尝试 [SomeClass initialize]
)。在您的代码中,运行时没有任何 react ,因为
self.myProperty
为零,所以没有错误。即使你有一个实现,那也是一样的。在绝大多数情况下,在链接期间会发现缺少实现文件,因为在系统中的某个地方您可能会调用 +alloc
。 .所以在实践中,这种情况根本不应该经常出现,而且这种罕见的情况很难在不破坏大量合法 ObjC 的情况下检测到。
关于ios - 是否有编译器标志以便链接器警告我这个? .h 文件,没有 .m 文件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34399620/