为啥 Apple 开发网站上的这个代码示例为同一个类声明了三个接口?
Posted
技术标签:
【中文标题】为啥 Apple 开发网站上的这个代码示例为同一个类声明了三个接口?【英文标题】:Why does this code example on Apple's dev site declare three interfaces for the same class?为什么 Apple 开发网站上的这个代码示例为同一个类声明了三个接口? 【发布时间】:2010-07-28 15:23:17 【问题描述】:我在尝试掌握 Objective-C 的同时潜入 ios 开发,但我仍处于那个阶段,无论在哪里,我都会看到对像我这样的资深 C 程序员没有任何意义的东西。在this Game Kit example on Apple's dev site中,其中一个头文件声明了一个类接口,三个不同的时间...
@interface SessionManager : NSObject <GKSessionDelegate>
NSString *sessionID;
GKSession *myGKSession;
NSString *currentConfPeerID;
NSMutableArray *peerList;
id lobbyDelegate;
id gameDelegate;
ConnectionState sessionState;
@property (nonatomic, readonly) NSString *currentConfPeerID;
@property (nonatomic, readonly) NSMutableArray *peerList;
@property (nonatomic, assign) id lobbyDelegate;
@property (nonatomic, assign) id gameDelegate;
- (void) setupSession;
- (void) connect:(NSString *)peerID;
- (BOOL) didAcceptInvitation;
- (void) didDeclineInvitation;
- (void) sendPacket:(NSData*)data ofType:(PacketType)type;
- (void) disconnectCurrentCall;
- (NSString *) displayNameForPeer:(NSString *)peerID;
@end
// Class extension for private methods.
@interface SessionManager ()
- (BOOL) comparePeerID:(NSString*)peerID;
- (BOOL) isReadyToStart;
- (void) voiceChatDidStart;
- (void) destroySession;
- (void) willTerminate:(NSNotification *)notification;
- (void) willResume:(NSNotification *)notification;
@end
@interface SessionManager (VoiceManager) <GKVoiceChatClient>
- (void) setupVoice;
@end
我看到每个接口都不一样,但是指定了相同的类名。
-
这是什么原因?
我在其他代码示例中也注意到了同样的行为,只不过不是在头文件中声明多个接口,而是在 .m 实现文件的顶部声明了一个额外的 @interface 块,通常在@实现块。为什么?
非常感谢您的智慧!
【问题讨论】:
【参考方案1】:这些被称为类别,您可以通过类名后面的括号看到它们。
它们用于将方法分组为块,而不是将它们全部放在一大堆中。它们也可以与主类声明分开放置。这在 .m 文件中特别有用,您可能需要为您的类创建实用程序方法,但出于任何原因您不希望它们对其他对象可见(因此您不要将它们放在 .h 中,即由其他类导入)。另一个常见用途是将对应于某个逻辑类别、非正式协议或您拥有的方法的方法分组。类别可以命名 (@interface MyClass (MyCategory)
) 或匿名 (@interface MyClass ()
)。后者通常用于标题中的通用私有方法。
(您需要类别来在 .m 中声明私有方法的原因是编译器知道这些方法 - 否则,当您尝试调用此类方法时会收到警告。)
此外,您可以使用类别将方法添加到现有类。例如,UIKit 在 NSString 上包含一个名为 NSString(UIStringDrawing) 的类别。或者,如果您想自己制作:
@interface NSString (MyFoo)
+ (NSString *)fooString;
@end
//... somewhere else...
@implementation NSString (MyFoo)
+ (NSString *)fooString return @"foo!";
@end
请注意,您不能添加带有类别的实例变量。
【讨论】:
【参考方案2】:它没有定义接口 3 次 - 只有一个接口。
您看到的是向类添加方法的类别
有一个定义属性和一些方法的基本接口——其中只有一个,它定义了对象在内存中的存储方式,并且是唯一需要的。
Objective C 在运行时查找方法。这些方法不需要在编译时找到,因此不需要在头文件/接口等中声明。如果没有声明它们并且您在代码中调用它们,那么您将收到编译时警告。
在这种情况下,一个名称为空的类别用于私有函数。我通常只把这个接口放在类的 .m 文件中,所以其他代码看不到头文件。
第二类是增加使SessionManager符合GKVoiceChatClient协议的方法。这样做的通常原因是将涵盖特定行为的代码组合在一起。
使用类别的另一个原因是向现有类(如 NSString)添加方法 - 您可以创建自己的类别添加方法,而无需像在许多其他 OO 语言(包括 Java 和 C++)中那样对类进行子类化
【讨论】:
【参考方案3】:我相信这是出于代码维护的目的...它更容易通过不同的标记接口查看,例如 (VoiceManager),它用于语音管理器设置和与之相关的方法,并且您有一个接口处理GK 委托方法以及与 gamekit 的任何交互......而不是查看一个巨大的接口文件并不得不挑选出你正在寻找的东西......他们也可以通过这种方式划分实现,这样更容易浏览和导航。
【讨论】:
【参考方案4】:按顺序:
第一个接口声明是实际的接口声明,将类声明为 NSObject 的子类并实现 GKSessionDelegate 协议。它还声明了实例变量和方法的选择。
第二个接口声明是一个类扩展。它可以被认为是一种匿名类别。所以我们暂时跳过它,然后再回来。
第三个接口是类别声明。类别允许您做两件事。它们允许您将类实现拆分到多个源文件中。在上面,您将拥有
@implementation SessionManager
// methods declared in the first @interface
@end
@implementation SessionManager(VoiceManager)
// methods declared in the third @interface
@end
两个@implementation 不必在同一个源文件中。
类别可以做的另一件事是允许您扩展已经存在的类。例如@interface NSString(MyStringMethods)...
回到类扩展,这有点像匿名类别。其中声明的方法的实现必须在主 @implementation
块中。类扩展的目的是允许您将私有 API 与类的头文件分开声明。如果我有只能从类中使用的方法,我通常会在顶部的 .m 文件中放置一个。虽然,请注意这只是一个编译时间限制。没有什么可以阻止任何人在运行时发送类扩展消息。
【讨论】:
以上是关于为啥 Apple 开发网站上的这个代码示例为同一个类声明了三个接口?的主要内容,如果未能解决你的问题,请参考以下文章
在 Apples MultipleDetailViews 示例中加载已经初始化的 DetailViewControllers