功能区别
- 扩展(Extension)
- 主要用于为类添加私有属性、方法声明及属性合成。它是类的一部分,和类的定义在同一个编译单元中,通常在
.m
文件中定义。例如,在一个Person
类的.m
文件中定义扩展来声明私有属性:
@interface Person ()
@property (nonatomic, strong) NSString *privateName;
@end
- 扩展声明的方法必须在类的实现部分实现,否则会导致链接错误。
- 类别(Category)
- 用于为现有的类添加新的方法,不需要访问类的源代码,也不需要继承。可以在运行时动态地为类添加方法。例如,为
NSString
类添加一个新的方法:
@interface NSString (Additions)
- (NSString *)reversedString;
@end
@implementation NSString (Additions)
- (NSString *)reversedString {
NSMutableString *reversed = [NSMutableString stringWithCapacity:self.length];
[self enumerateSubstringsInRange:NSMakeRange(0, self.length) options:NSStringEnumerationByComposedCharacterSequences usingBlock:^(NSString *substring, NSRange substringRange, NSRange enclosingRange, BOOL *stop) {
[reversed insertString:substring atIndex:0];
}];
return reversed;
}
@end
- 类别不能添加实例变量,因为在运行时类别加载时,类的布局已经确定,无法再添加新的实例变量。不过可以通过关联对象(Associated Objects)模拟添加实例变量。
实现机制区别
- 扩展(Extension)
- 扩展是在编译期处理的,它作为类定义的一部分,编译器会将扩展中的声明和类的其他声明合并处理。所以扩展声明的方法和属性与类本身声明的方法和属性在编译和运行时的地位是一样的。
- 类别(Category)
- 类别是在运行时处理的。当程序运行时,runtime会将类别中的方法添加到类的方法列表中。由于类别加载的顺序问题,如果多个类别对同一个类添加了同名方法,最后加载的类别中的方法会覆盖之前的方法。
优先选择使用Extension而非Category的情况
- 添加私有属性和方法:当你希望为类添加仅在类内部使用的属性和方法时,应优先使用扩展。例如,一个
Calculator
类,有一些用于内部计算逻辑的私有方法和属性,使用扩展来定义这些内容:
@interface Calculator ()
@property (nonatomic, assign) double intermediateResult;
- (double)privateCalculate:(double)a and:(double)b;
@end
@implementation Calculator
- (double)privateCalculate:(double)a and:(double)b {
return a + b;
}
// 其他公开方法实现中调用私有方法和属性
@end
- 属性合成:如果需要为类添加私有属性并进行属性合成(例如生成对应的
getter
和setter
方法),扩展是更好的选择。因为类别不能合成属性,只能声明方法来模拟属性访问。