要求在id中存在方法

时间:2011-02-27 11:29:10

标签: iphone objective-c cocoa-touch protocols categories

情况

我的自定义控制器类有以下方法:

- (void)requestViewControllerWithIdentifier:(NSString *)identifier fromObject:(id)object;

这会导致object收到此消息:

- (UIViewController *)viewControllerWithIdentifier:(NSString *)identifier;

问题

现在,object只是一个id,因此无法保证它实际实现了该方法,并且在发送消息时收到编译器警告。


解决方案

我自己想出了两个解决方案:

  1. 使用协议。
  2. id设为NSObject并为NSObject创建一个类别。
  3. 它们可能都是很好的解决方案,我不介意选择其中之一,但是......


    问题

    ......我注意到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;
    

    不使用任何协议或类别!我想知道他们为什么这样做以及为什么这样做?为什么不使用协议?他们是否以其他方式强制执行该方法?如果我这样做,我怎么能抑制编译器警告?

2 个答案:

答案 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有一个很好的解释。