
动态类—在运行时确定对象的类
动态绑定—在运行时确定要调用的方法
动态装载—在运行时为程序增加新的模块
@interface
或@implementation
导向符之后,且放在园括号中。举例来说,假定您希望为NSArray
类增加一个方法,以便用更加结构化的方式打印集合的描述。那么您可以在范畴的头文件中书写如下的声明代码:
-
#import <Foundation/NSArray.h> // if Foundation not already imported
@interface NSArray (PrettyPrintElements)
- (NSString *)prettyPrintDescription;
@end
然后在实现文件中书写如下代码:
#import "PrettyPrintCategory.h"
@implementation NSArray (PrettyPrintElements)
- (NSString *)prettyPrintDescription {
// implementation code here...
}
@end
范畴有一些限制。您不能通过范畴为类添加新的实例变量。虽然范畴方法可以覆盖现有的方法,但这并不是推荐的做法,特别是当您希望对现有行为进行增强的时候。一个原因是范畴方法是类接口的一部分,因此无法通过向
super
发送消息来获取类中已经定义的行为。如果您需要改变一个类的现有方法的行为,更好的方法是生成一个该类的子类。您可以通过范畴来为根类—
NSObject
—添加方法。通过这种方式添加的方法可以用于与该代码相连接的所有实例和类对象。非正式的协议—Cocoa委托机制的基础—在NSObject
类中声明为范畴。然而,这种在使用上的广泛适用也有它的风险。您通过范畴向NSObject
添加的行为可能会有意料不到的结果,可能导致崩溃,数据损坏,甚至更坏的结果。--------------------------------------------------------------------------------------------------------------------------------------------
协议
Objective-C的另一个扩展称为协议,它非常象Java中的接口。两者都是通过一个简单的方法声明列表发布一个接口,任何类都可以选择实现。协议中的方法通过其它类实例发送的消息来进行调用。
协议的主要价值和范畴一样,在于它可以作为子类化的又一个选择。它们带来了C++多重继承的一些优点,使接口(如果不是实现的话)可以得到共享。协议是一个类在声明接口的同时隐藏自身的一种方式。接口可以暴露一个类提供的所有(通常是这种情况)或部分服务。类层次中的其它类都可以通过实现协议中的方法来访问协议发布的服务,不一定和协议类有继承关系(甚至不一定具有相同的根类)。通过协议,一个类即使对另一个类的身份(也就是类的类型)一无所知,也可以和它进行由协议定义的特定目的的交流。
有两种类型的协议:正式和非正式协议。非正式协议在"范畴"部分中已经简单介绍了,它们是
NSObject
类中定义的范畴。因此每个以NSObject
为根类的对象(和类对象)都隐式采纳了范畴中发布的接口。和正式协议不同,一个类不必实现非正式协议中的每个方法,而是只实现它感兴趣的方法就可以了。为了使非正式协议正确工作,声明非正式协议的类在向某个目标对象发送协议消息之前,必须首先向它发送respondsToSelector:
消息并得到肯定的回答(如果目标对象没有实现相应的方法,则产生一个运行时例外)。 Cocoa中的“协议”通常指的是正式协议。它使一个类可以正式地声明一个方法列表,作为向外提供服务的接口。Objective-C语言和运行系统支持正式协议;编译器可以根据协议进行类型检查,对象可以在运行时进行内省,以确认是否遵循某个协议。正式协议有自己的专用术语和语法。术语方面,提供者和客户的意义有所不同:
在Objective-C中,声明和采纳协议都有自己的语法。协议的声明必须使用编译导向符
@protocol
。下面的例子显示了NSCoding
协议(在Foundation框架的NSObject.h
头文件中)的声明方式:@protocol NSCoding
- (void)encodeWithCoder:(NSCoder *)aCoder;
- (id)initWithCoder:(NSCoder *)aDecoder;
@end
协议的声明类不需要实现这些方法,但应该对遵循该协议的对象方法进行调用。
如果一个类要采纳某个协议,需要在在
@interface
导向符后、紧接着超类的位置上指定协议的名称,并包含在尖括号中。一个类可以采纳多个协议,不同的协议之间用逗号分隔。下面是Foundation框架中的NSData
类采纳三个协议的方式:@interface NSData : NSObject <NSCopying, NSMutableCopying, NSCoding>
通过采纳这些协议,
NSData
许诺自己要实现协议中声明的所有方法。范畴也可以采纳协议,对协议的采纳将成为类定义的一部分。Objective-C通过类遵循的协议和类继承的超类来定义类的类型。您可以通过发送
conformsToProtocol:
消息来检查一个类是否遵循特定的协议:if ([anObject conformsToProtocol:@protocol(NSCoding)]) {
// do something appropriate
}
在类型声明—方法、实例变量、或函数中,您可以将遵循的协议作为类型的一部分来指定。这样您就可以通过编译器来得到另一个级别的类型检查,这种检查比较抽象,因为它不和特定的实现相关联。您可以使用与协议采纳相同的语法规则,即把协议的名称放在尖括号中,通过这种语法可以在类型中指定遵循的协议。您常常会看到在这些声明中使用了动态对象类型
id
,例如:- (void)draggingEnded:(id <NSDraggingInfo>)sender;
这里,参数中引用的对象可以是任意类型的类,但是必须遵循
NSDraggingInfo
协议。除了目前为止已经提到的协议之外,Cocoa还提供了几个协议的例子。一个有趣的例子就是
NSObject
协议。可以想象得到的是,NSObject
类采纳了这个协议,还有一个根类—NSProxy
—也采纳了这个协议。通过这个协议,NSProxy
类可以和Objective-C运行环境的一部分进行交互,包括引用计数、内省、和对象行为的其它基础部分。正式协议有其自己的限制。如果协议声明的方法列表随着时间而增长,协议的采纳者就会不再遵循该协议。因此,Cocoa中的正式协议被用于稳定的方法集合,比如
NSCopying
和NSCoding
。如果您预期协议方法会增多,则可以声明为非正式协议,而不是正式协议。本文摘自