类别(category)
category可以理解为给一个类“打补丁”的方法。它可以在不创建子类且不访问类原有源代码的前提下,给已有的类添加新的方法。
类别同样由接口和实现两部分组成,接口部分语法如下:
@interface 已有类 (类别名)
//方法定义
...
@end
实现部分语法如下:
@implementation 已有类 (类别名)
//方法实现
...
@end
如下程序,为NSNumber类添加加减乘除方法并进行了测试。
#import <Foundation/Foundation.h>
//为NSNumber定义一个名为fk的类别
@interface NSNumber (fk)
- (NSNumber*) add: (double) num2;
- (NSNumber*) substract: (double) num2;
- (NSNumber*) multiply: (double) num2;
- (NSNumber*) divide: (double) num2;
@end
//为类别fk提供实现部分
@implementation NSNumber (fk)
- (NSNumber*) add: (double) num2{
return [NSNumber numberWithDouble:([self doubleValue] + num2)];
}
- (NSNumber*) substract: (double) num2{
return [NSNumber numberWithDouble:([self doubleValue] - num2)];
}
- (NSNumber*) multiply: (double) num2{
return [NSNumber numberWithDouble:([self doubleValue] * num2)];
}
- (NSNumber*) divide: (double) num2{
return [NSNumber numberWithDouble:([self doubleValue] / num2)];
}
@end
int main(int argc, char* argv[]){
@autoreleasepool{
NSNumber* myNum = [NSNumber numberWithInt:3];
NSNumber* add = [myNum add:2.4];
NSLog(@"%@", add);
NSNumber* substract = [myNum substract:2.4];
NSLog(@"%@", substract);
NSNumber* multiply = [myNum multiply:2.4];
NSLog(@"%@", multiply);
NSNumber* divide = [myNum divide:2.4];
NSLog(@"%@", divide);
}
}
一般习惯将类别的接口文件命名为
类名+类别名.h
,对应的实现文件命名为类名+类别名.m
的形式。
类别通常有如下三种用法:
- 利用类别对类进行模块化设计。
- 利用类别来调用私有方法。
- 利用类别来实现非正式协议。
利用类别对类进行模块化设计
通常,将一个类的接口部分放在一个.h
文件里,实现部分放在一个.m
文件。但如果某个类非常大时,其所有的实现部分代码放在一个.m
文件将导致这个文件非常大,维护不方便。针对这种情况,可以使用category将庞大的类模块化,把不同的部分放在不同的.m
文件里,提高可维护性。
利用类别来调用私有方法
定义类时,没有在接口部分定义而是直接在类实现部分定义的方法相当于私有方法,通常不允许被调用。但是,Objective-C并没有真正意义上的私有方法,调用这些方法有两种方法:
- 通过NSObject的
performSelector:
方法来执行动态调用; - 通过类别来定义前向引用,从而实现对私有方法的调用。
方法1会完全避开编译器的语法检查,不见得是一种好的做法。
@interface FKItem : NSObject
@property (nonatomic, assign) double price;
@end
@implementation FKItem
- (double) calDiscount:(double) discount{
return self.price * discount;
}
@end
如上程序,calDiscount:
方法就是Objective-C中所谓的私有方法,直接通过FKItem对象调用该方法会导致错误。
为了能正确调用这个方法,可以在main()函数前增加如下类别定义:
@interface FKItem (fk)
- (double) calDiscount:(double) discount;
@end
类别的方法并不强制程序去实现它。(实际上,FKItem类的实现部分已经实现了该方法。)
这样就可以通过FKItem的对象调用这个方法了。
扩展(Extension)
扩展相当于匿名类别。定义扩展的语法格式如下:
@interface 已有类 ()
{
实例变量
}
// 方法定义
...
@end
类别与扩展的区别
类别 | 扩展 |
---|---|
有名 | 匿名 |
通常有单独的.h 和.m 文件 | 通常有单独的.h 文件,而实现由类实现部分同时实现类接口和扩展中定义的方法 |
用于为现有类添加新方法,不允许额外定义实例变量,也不可用@property 合成属性 | 可以额外增加实例变量,也可用@property 合成属性 |