1.类别
很多时候你会发现你想为一个已经存在的类增加方法,并且希望这个方法适用于该类的所有子类,这时候你应该会用到category。
category用于向已有的类增加方法,你甚至不需要知道该类的实现代码
类别一般是生命在一个单独的头文件中并且在一个单独的代码文件中实现,比如下面的例子,尽管通过category添加的方法适用于对应类的所有实例以及他的子类,在你需要使用这些额外方法的时候你也需要引用category的头文件,不然你会收到编译警告和错误。下面是一个实现category的例子:
#import "XYZPerson.h" @interface XYZPerson (XYZPersonNameDisplayAdditions) - (NSString *)lastNameFirstNameString; @end
#import "XYZPerson+XYZPersonNameDisplayAdditions.h" @implementation XYZPerson (XYZPersonNameDisplayAdditions) - (NSString *)lastNameFirstNameString { return [NSString stringWithFormat:@"%@, %@", self.lastName, self.firstName]; } @end
你除了可以使用类别向已知的类增加方法外,你还可以使用类别将一个复杂的类的实现放到多个文件中。比如NSWindow,改类的文档在打印时将超过60页。如果将NSWindow类的所有代码组织在一个文件中,即使是Cocoa得开发团队也会觉得其过于庞大而难以驾驭,这时候他会将一些相关方法的实现放到一个类别中。查看文档我们可以看到
@interface NSWindow : NSResponder 然后是一大堆类别,其中包括下面这些 @interface NSWindow(NSKeyboardUI) @interface NSWindow(NSToolbarSupport)
在类别中包含一个属性声明是合法的语法,但是在类别中声明额外的实例变量是不可能的。也就是说编译器不会自动生成任何实例变量,也不会生成任何属性访问方法,你可以在类别视线中写你自己的实现方法,但是你不可能获取属性的变量,除非该属性本来就是类别所扩充类的属性。下面举例说明
@interface ZZFViewController (test) @property (nonatomic, strong) NSString *test; @end上面这种写法是没有问题,但是编译器没有自动生成_test实例变量,它实际上是为ZZFViewController声明了两个方法。
- (void)setTest:(NSString *)test - (NSString *)test如果想为类增加属性就需要用到下面将要介绍的类扩展。
2.扩展
@implementation
中实现,所以如果你没有源码的话是无法使用扩展的,比如你不能为NSString增加一个扩展
@interface ClassName () @end因为花括号里面没有名字,所有有的时候扩展也被称为匿名类别。
Unlike regular categories, a class extension can add its own properties and instance variables to a class. If you declare a property in a class extension, like this:
@interface XYZPerson () @property NSObject *extraProperty; @end你还可以使用类扩展隐藏一些私有信息,比如你可以讲一个属性在头文件中声明为只读,但是在类扩展中将其设置为可读写的,这样该属性对于外边来说是只读的,但对他自己是可读写的。如下:
@interface XYZPerson : NSObject ... @property (readonly) NSString *uniqueIdentifier; - (void)assignUniqueIdentifier; @end
@interface XYZPerson () @property (readwrite) NSString *uniqueIdentifier; @end @implementation XYZPerson ... @end