iOS的分类(Category)
一:Category的底层结构
定义在objc-runtime-new.h中:
struct category_t {
const char *name;
classref_t cls;
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
method_list_t*methodsForMeta(boolisMeta) {
if(isMeta)returnclassMethods;
else return instanceMethods;
}
property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
二:Category的加载处理的过程
1.通过Runtime加载某个类的所有Category数据
2.把所有Category的方法、属性、协议数据,合并到一个大数组中
----后边参与编译的Category数据,会在数组的前边
3.将合并的分类数据(方法、协议、属性),插入到类原来数据的前面
接下来证明一下方法是否真的可以添加成功:
创建一个NSObject的分类
NSObject+KS.h:
@interface NSObject (KS)
-(void)Turbo;
-(void)KS;
@end
NSObject+KS.m:
@implementation NSObject (KS)
- (void)Turbo{
}
- (void)KS{
}
@end
ViewController.m中:
#import "ViewController.h"
#import "NSObject+KS.h"
#import
@interface ViewController ()
@property (nonatomic, strong) NSObject * obj;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
[self getMethod];
}
-(void)getMethod{
unsignedintcount;
Method *methods = class_copyMethodList([NSObject class], &count);
for(inti =0; i < count; i++)
{
Methodmethod = methods[i];
SELselector =method_getName(method);
NSString*name =NSStringFromSelector(selector);
NSLog(@"%@",name);
}
}
@end
打印结果如下:
打印结果
可以看出、我们自己添加的方法确实合并到了NSObject的类中,并且还处于头部的位置,由此可以证明上边的理论是正确的!
三:+load方法
1. +load方法会在runtime加载类、分类的时候调用
2. 每个类、分类的+load方法,在程序运行过程中只调用一次
3. 调用顺序:
- 先调用类的+load
-- 按照边币先后顺序调用(先编译,先调用)
-- 调用子类的+load之前会先调用父类的+load
- 再调用分类的+load
-- 按照编译的先后顺序调用(先编译,先调用)
+load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用
四:+initialize方法
1. +initialize方法会在类第一次接收到消息的时候调用
2. 调用顺序
- 先调用父类的+initialize,再调用子类的+initialize
- 先初始化父类,再初始化子类,每个类只会初始化1次
五:+initialize和+load的区别?
+initialize和+load的很大区别是,+initialize是通过objc_msgSend进行调用的,所以会有以下特点:
1. 如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次)
2. 如果分类实现了+initialize,就会覆盖类本身的+initialize调用
六:如何给分类添加成员变量?
默认情况下,因为分类底层结构的限制,不能添加成员变量到分类中。但是可以通过关联对象来间接实现。
1. 添加关联对象
objc_setAssociatedObject(id _Nonnull object, const void * _Nonnull key, id_Nullablevalue,objc_AssociationPolicypolicy)
2. 获取关联对象
objc_getAssociatedObject(id _Nonnull object, const void * _Nonnull key)
3. 移除所有的关联对象
objc_removeAssociatedObjects(id _Nonnull object)
补充:
key的常见方法:
static void *MyKey = &MyKey;
objc_setAssociatedObject(obj, MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, MyKey)
static char MyKey;
objc_setAssociatedObject(obj, &MyKey, value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, &MyKey)
使用属性名作为key
objc_setAssociatedObject(obj, @"property", value, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
objc_getAssociatedObject(obj, @"property");
使用get方法的@selecor作为key
objc_setAssociatedObject(obj, @selector(getter), value, OBJC_ASSOCIATION_RETAIN_NONATOMIC)
objc_getAssociatedObject(obj, @selector(getter))
objc_AssociationPolicypolicy
关联对象的原理:
代码实现给分类添加属性:
NSObject+KS.h
@interface NSObject (KS)
@property (nonatomic, strong) NSString * ksname;
@end
NSObject+KS.m
- (NSString *)ksname{
return objc_getAssociatedObject(self, @"ksname");
}
- (void)setKsname:(NSString*)ksname{
objc_setAssociatedObject(self, @"ksname", ksname, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
ViewController.m 中使用
- (void)viewDidLoad {
[super viewDidLoad];
NSObject * obj = [[NSObject alloc] init];
obj.ksname=@"呵呵";
NSLog(@"%@",obj.ksname);
}
打印结果
打印结果
七:关联对象的原理
原理
原理