我的自定义控制器类有以下方法:
- (void)requestViewControllerWithIdentifier:(NSString *)identifier fromObject:(id)object;
这会导致object
收到此消息:
- (UIViewController *)viewControllerWithIdentifier:(NSString *)identifier;
现在,object
只是一个id
,因此无法保证它实际实现了该方法,并且在发送消息时收到编译器警告。
我自己想出了两个解决方案:
id
设为NSObject
并为NSObject
创建一个类别。它们可能都是很好的解决方案,我不介意选择其中之一,但是......
......我注意到Apple在他们的GameKit API中做了一些奇怪的事情。 GKSession
有以下方法:
- (void)setDataReceiveHandler:(id)handler withContext:(void *)context
handler
只是id
,但Apple确实需要它来实现此方法:
- (void) receiveData:(NSData *)data fromPeer:(NSString *)peer inSession: (GKSession *)session context:(void *)context;
不使用任何协议或类别!我想知道他们为什么这样做以及为什么这样做?为什么不使用协议?他们是否以其他方式强制执行该方法?如果我这样做,我怎么能抑制编译器警告?
答案 0 :(得分:2)
您可以将类型id
的变量分配给任何对象类型。如果您知道它必须实现给定的协议,您可以将其分配给方法中的该类型的变量,并在该变量上调用该方法。
作为一个设计点,我想说最好将协议显式化并将其外部化给调用者,以便编译器可以正确地进行类型检查。 Apple的代码的某些部分在这方面比其他部分更好:从你所说的GameKit非常有用的东西。
定义类别不是你想要做的,因为它告诉编译器你将把方法添加到每个NSObject。
如果你有这个协议:
@protocol YourProtocol <NSObject>
- (UIViewController *)viewControllerWithIdentifier:(NSString *)identifier;
@end
并将您的方法定义为:
- (void)requestViewControllerWithIdentifier:(NSString *)identifier fromObject:(id <YourProtocol>)object;
它可能会做你想要的。在这种情况下可能不是绝对必要的,但通常最好让您的协议扩展NSObject
协议(如上所述),以便您可以调用有用的内容,例如-retain
,-release
,-autorelease
和-respondsToSelector
。声明如下方法的替代方法可防止用户使用NSProxy
- rooted对象作为object
参数。
- (void)requestViewControllerWithIdentifier:(NSString *)identifier fromObject:(NSObject <YourProtocol> *)object;
如果它只是供您自己使用而您没有使用代理对象,这可能非常方便,但您应该在公共API中避免使用它。
答案 1 :(得分:0)
我认为您寻求的答案是正式和非正式协议之间的区别。 This answer有一个很好的解释。