一、category:
1、分类,可以为一个已知.m文件源码的类添加分类,也可以为只暴露.h文件不知道.m实现的类(系统类)添加分类。
2、给某个类添加category会生成.h和.m两个文件,文件名为Person+son.h和Person+son.m这种形式,声明和实现部分形如@interface Person (son)和@implementation Person (son),即比普通的类文件多了个括号,括号里是分类的名称
3、不能添加属性的原因是category是运行时添加,而实例变量是在编译期间添加的,因为属性里也有_ivar实例变量,所以不能添加属性,但是可以通过runtime动态为这个属性设置getter和setter方法,但是这个属性还是没有实例变量的,直接访问_ivar是不可以的。
4、添加属性编译期间不会报错,runtime时访问会报错(如果添加分类的同时还添加了一个extension,extension添加了同名属性则category添加了这个属性也没问题)
5、添加原类已有属性是没问题的。
6、分类添加的方法会放在方法列表的最前面,从而拦截了被分类的原类里的同名方法,如果有两个分类都为一个类重写了同一个方法,会根据这两个分类在xcode文件列表的前后顺序有关,位置靠后的那个分类会拦截这个同名方法。
例如 有个Person类,他有一个drink方法
@interface Person : NSObject
@property (nonatomic,strong) NSString *age;
- (void)drink;
@end
@implementation Person
- (void)drink{
NSLog(@"drink");
}
@end
为Person类添加一个分类A,在这个分类的.m文件里重写drink方法
@implementation Person (A)
- (void)drink{
NSLog(@"Adrink");
}
@end
再为Person类添加一个分类B,在这个分类的.m文件里重写drink方法
@implementation Person (B)
- (void)drink{
NSLog(@"Bdrink");
}
@end
那么当我们调用drink方法时会调用哪个类的呢?
Person *p = [Person new];
[p drink];
结果是打印分类B的drink方法
打印
可以看一下A和B在文件目录中的位置1
位置.png
再看一下A和B在文件目录中的位置2
位置
这是因为A的位置在B的前面,在编译阶段A被先加载,到了runtime时A的drink方法先被添加到类的方法列表的最前面,B的drink方法会被添加到A的前面,所以后添加的B的drink方法被先执行了,故拦截了A的drink和原来类的drink,消息传递到B就执行了。
二、extension:
1、拓展 只生成一个.h文件,文件名形如Person+C, 相当于私有属性和方法(即类似于.m里 @interface里的属性和方法的声明)
2、不能添加已有的属性 (相当于一个文件有两个同名属性,当然不行), 添加已有的属性会在编译期间报错 ,可以添加新属性
3、可以添加同名方法(没有意义,因为extension没有自己的.m文件,方法实现还是依靠原类)、新方法。
网友评论