假设:
@required
在编译时,有关此方法的信息是已知的:即它是必需的,并且此类和此类可能扩展的任何其他类不会实现它。
为什么在这种情况下编译器会发出警告而不是错误?
答案 0 :(得分:6)
只有在编译器无法继续时才会发出错误,因为某些内容非常错误。
在Objective-C中调用方法时,方法查找是在运行时完成的,而不是在C ++编译期间完成的。在Objective-C中,“消息”只是发送到对象,类似于obj.executeCommand("Hey, can you execute function <name> for me?")
。在C ++中,对象将以obj.<name>()
之类的方式直接调用。在Objective-C的情况下,调用executeCommand()方法,该方法存在。在C ++的情况下,函数被调用但它不存在。这些是在编译器级别链接的方法,这意味着它们都成为内存地址而不是名称。 executeCommand
变为0x12345678,但仍然使用相同的消息("execute function <name>"
)。
这可能非常令人困惑,但它与方法以不同语言实现的方式有关。
答案 1 :(得分:2)
如果你对此感到强烈,为什么不打开-Werror
?
答案 2 :(得分:0)
我不知道真正的答案,但这是一个与之相反的用例。
如果您在类别???
中实现了所有协议方法,该怎么办?主接口声明采用协议,但协议方法实现属于一个类别。 这是有效的代码,但如果编译器严格,则会显示编译错误!
答案 3 :(得分:0)
Objective-C是一种动态语言。实现的概念与静态语言不同 在大多数情况下,我们大多数人都在@implementation ... @end块中执行代码 但是,如果找不到方法怎么办?然后一个对象有机会动态处理它。
想象一下,你有一个音效播放器的界面:
@protocol FX
- (void)playBeep;
- (void)playSiren;
- (void)playHonk;
@end
实现可以播放文件Beep.mp3,Siren.mp3,Honk.mp3,但不是实现每个方法,而是覆盖 -forwardInvocation:并解析选择器字符串,像这样的伪代码:
NSString *selName = NSStringFromSelector([invocation selector]);
if ([selName startsWith:@"play"]) {
NSString filename = fileNameFromSelector(selName);
[self playSoundFileNamed:filename];
}
这可能看似人为,但是一旦你开始使用该语言的动态功能,你就会开始找到越来越多有意义的地方。我的意思是,从长远来看,这项努力是否有帮助?
在上面的例子中,只需在接口中添加一个-sound *方法名称,然后输入一个适当命名的声音文件。它只是有效。
个人实验的另一个例子:如何以更自然的方式处理核心数据实体。我想做这个: NSArray * people = [Person findAllWithNameLike:@“B%”]; 而不是与谓词混淆,获取请求等 但我不想在代码中定义方法的每个排列。
如果我想构建XML构建器怎么样?我会看一个动态的方法。它很好地服务于Groovy Builders(以Groovy / Grails为例)。
最后一个例子:我有一个特征系统,我可以用方法组的形式定义行为,让我的对象吸收这种行为。因此,虽然编译器没有看到我的对象符合的接口的实现,但实现是使用Objective-C运行时从trait类注入到它中的。我为什么要这样做?我发现许多委托方法都是锅炉板,但同时,每种情况的单个基类都不够灵活。我的'samples'编译并运行:)而不是从代码示例中剪切和粘贴,并且使用该特征在所有项目中反映任何更改。
要真正理解为什么所有这些都可供您使用,值得玩Smalltalk环境(搜索Pharo或Squeak)。这就是Objective-C的根源所在。
最后,要停止这些警告:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wprotocol"
@implementation ... @end
#pragma clang diagnostic pop
答案 4 :(得分:0)
因为在设计不良的协议中有时会出现虚假的“必需”方法。 它们应该是可选的,但有人坚持认为它们是“必需的”。 因此,使这成为运行时问题而不是编译错误是非常明智的。