我们再来看一下load方法的调用源码
我们看到load方法中直接拿到load方法的内存地址直接调用方法,不在是通过消息发送机制调用
我们可以看到分类中也是通过直接拿到load方法的地址进行调用。因此正如我们之前试验的一样,分类中重写load方法,并不会优先调用分类的load方法,而不调用本类中的load方法了。
RunTime 为 Category 动态关联对象
使用RunTime给系统的类添加属性,首先需要了解对象与属性的关系。我们通过之前的学习知道,对象一开始初始化的时候其属性为nil,给属性赋值其实就是让属性指向一块存储内容的内存,使这个对象的属性跟这块内存产生一种关联。
那么如果想动态的添加属性,其实就是动态的产生某种关联就好了。而想要给系统的类添加属性,只能通过分类。
这里给NSObject添加name属性,创建NSObject的分类
我们可以使用@property给分类添加属性
@property(nonatomic,strong)NSString *name;
通过探寻Category的本质我们知道,虽然在分类中可以写@property 添加属性,但是不会自动生成私有属性,也不会生成set,get方法的实现,只会生成set,get的声明,需要我们自己去实现。
方法一:我们可以通过使用静态全局变量给分类添加属性
static NSString *_name; - (void)setName:(NSString *)name { _name = name; } - (NSString *)name { return _name; }
但是这样_name静态全局变量与类并没有关联,无论对象创建与销毁,只要程序在运行_name变量就存在,并不是真正意义上的属性。
方法二:使用RunTime动态添加属性 RunTime提供了动态添加属性和获得属性的方法。
-(void)setName:(NSString *)name { objc_setAssociatedObject(self, @"name",name, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } -(NSString *)name { return objc_getAssociatedObject(self, @"name"); }
- 1.动态添加属性
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
参数一:id object : 给哪个对象添加属性,这里要给自己添加属性,用self。
参数二:void * == id key : 属性名,根据key获取关联对象的属性的值,在**objc_getAssociatedObject中通过次key获得属性的值并返回。
参数三:id value** : 关联的值,也就是set方法传入的值给属性去保存。
参数四:objc_AssociationPolicy policy : 策略,属性以什么形式保存。
有以下几种
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { OBJC_ASSOCIATION_ASSIGN = 0, // 指定一个弱引用相关联的对象 OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // 指定相关对象的强引用,非原子性 OBJC_ASSOCIATION_COPY_NONATOMIC = 3, // 指定相关的对象被复制,非原子性 OBJC_ASSOCIATION_RETAIN = 01401, // 指定相关对象的强引用,原子性 OBJC_ASSOCIATION_COPY = 01403 // 指定相关的对象被复制,原子性 };
key值只要是一个指针即可,我们可以传入@selector(name)
- 2.获得属性
objc_getAssociatedObject(id object, const void *key);
参数一:id object : 获取哪个对象里面的关联的属性。
参数二:void * == id key : 什么属性,与objc_setAssociatedObject中的key相对应,即通过key值取出value。
- 3.移除所有关联对象
- (void)removeAssociatedObjects { // 移除所有关联对象 objc_removeAssociatedObjects(self); }
此时已经成功给NSObject添加name属性,并且NSObject对象可以通过点语法为属性赋值。
NSObject *objc = [[NSObject alloc]init]; objc.name = @"xx_cc"; NSLog(@"%@",objc.name);
可以看出关联对象的使用非常简单,接下来我们来探寻关联对象的底层原理
objc_setAssociatedObject函数
来到runtime源码,首先找到objc_setAssociatedObject函数,看一下其实现
我们看到其实内部调用的是_object_set_associative_reference函数,我们来到_object_set_associative_reference函数中
_object_set_associative_reference函数
_object_set_associative_reference函数内部我们可以全部找到我们上面说过的实现关联对象技术的核心对象。接下来我们来一个一个看其内部实现原理探寻他们之间的关系。
AssociationsManager
通过AssociationsManager内部源码发现,AssociationsManager内部有一个AssociationsHashMap对象。
AssociationsHashMap
我们来看一下AssociationsHashMap内部的源码。
通过AssociationsHashMap内部源码我们发现AssociationsHashMap继承自unordered_map首先来看一下unordered_map内的源码
从unordered_map源码中我们可以看出_Key和_Tp也就是前两个参数对应着map中的Key和Value,那么对照上面AssociationsHashMap内源码发现_Key中传入的是disguised_ptr_t,_Tp中传入的值则为ObjectAssociationMap*。
紧接着我们来到ObjectAssociationMap中,上图中ObjectAssociationMap已经标记出,我们发现ObjectAssociationMap中同样以key、Value的方式存储着ObjcAssociation。
接着我们来到ObjcAssociation中