目录
1. load方法分析
2. +load和+initialize区别
3. 给Category“添加成员变量”
4. 关联对象实现原理
+load方法分析:
- 调用时机:load方法会在runtime加载类、分类时调用,因为所有的类、分类都会在程序启动时被载入内存,所以所有的类、分类不管有没有被用到都会调用一次load。
问题1:
- 没有继承关系时,load方法按照类的编译顺序执行;
- 有继承关系时,父类和子类的load方法,无论怎么调整编译顺序,父类的load方法总是先执行?
下面时person是student的父类,student的编译顺序在person前面,person却先调用load
问题2:
上篇文章有分析过,在runtime加载时会把所有方法合并到数组,分类的方法总是会在类方法前面,如果有相同的方法,只会调用最后编译的分类方法,然而对于+load方法,测试发现所有的分类和类调用到了load方法,这是为什么呢?
这里我们还是源码分析:Objc源码下载地址 主要查看源码的文件和方法
objc-os.mm 文件 (主要是初始化)
_objc_init
objc-runtime-new.mm文件
load_images //加载方法
///加载load方法前的一些处理
prepare_load_methods //按照编译顺序取出类放在一个list中
schedule_class_load, //递归方法,先找到父类,最后再到子类
add_class_to_loadable_list //添加进load数组中
///加载load方法
call_load_methods //加载所有load方法
call_class_loads //加载所有类的load方法
call_category_loads //加载所有分类的load方法
问题一的分析:
方法查看顺序
load_images ->prepare_load_methods ->schedule_class_load ->add_class_to_loadable_list
通过上面的源码分析,已经知道:
类按照编译顺序将类依次加入到list,然后遍历list,取出每个类,再通过递归方法找到父类,然后全部放入loadable_classes中,
所以loadable_classes里面的类排序就是:
没有继承关系的类,按编译顺序;有继承关系的,父类在前,子类在后。
接着我们先来
问题二的分析:
方法查看顺序
load_images ->call_load_methods ->call_class_loads ->call_category_loads
从上面的源码分析我们可以知道:
- 所有类的load方法先调用完,才会调用分类的load方法
- 每个类和分类都有一个load方法函数指针,是直接通过函数指针调用,并不会像其他方法会合并到一个方法列表里,所以所有的类和分类都会执行load方法,不存在被“覆盖”。
- loadable_classes按顺序遍历调用类的load方法:先编译的类, 优先调用load, 调用子类的load之前, 会先调用父类的load 当包含的程序库载入系统时,就会执行此方法,并且此过程通常是在程序启动的时候执行。
+load和+initialize区别
-
在程序启动的时候就会执行一次所有类和分类的+load方法,而+initialize方法只会在类第一次接收到消息时调用一次。
-
+load是通过调用函数地址直接调用,所以不存在被“覆盖”问题,每个类/分类都会调用到;
+initialize是通过objc_msgSend进行调用的所以和之前讲的类方法一样,通过isa指针查找方法实现,方法都最终会合并到一个方法列表中,会存在“覆盖”的情况:
a. 如果子类没有实现+initialize,会调用父类的+initialize(所以父类的+initialize可能会被调用多次);
b. 如果分类实现了+initialize,就覆盖类本身的+initialize调用.
给Category“添加成员变量”
- 一般情况,因为分类底层结构的限制,不能添加成员变量到分类中,但是可以通过Runtime API 关联对象实现;
主要是三个API
//添加关联对象
/**
参数含义:
id object :表示关联者,是一个对象,一般是传self
const void *key :获取被关联者的索引key,
id value :被关联者
objc_AssociationPolicy policy: 关联策略
OBJC_ASSOCIATION_ASSIGN , = assign
OBJC_ASSOCIATION_RETAIN_NONATOMIC, = nonatomic, strong
OBJC_ASSOCIATION_COPY_NONATOMIC, = nonatomic, copy
OBJC_ASSOCIATION_RETAIN atomic, = strong
OBJC_ASSOCIATION_COPY atomic, = copy
**/
void objc_setAssociatedObject(id object, const void * key,
id value, objc_AssociationPolicy policy)
//获取关联对象
id objc_getAssociatedObject(id object, const void * key)
//移除所有关联对象
void objc_removeAssociatedObjects(id object)
如下在一个分类添加name属性,在分类中property不会实现set和get方法,关联对象主要也是帮分类声明的属性手动生成set和get方法。
.h
@interface Person (Test)
@property(nonatomic,strong)NSString * name;
@end
.m
#import <objc/runtime.h>
@implementation Person (Test)
-(void)setName:(NSString *)name{
//const void * key直接传@selector(name)确保唯一性
objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
//_cmd代表当前方法的selector,即@selector(name)
return objc_getAssociatedObject(self,_cmd);
}
@end
关联对象实现原理
实现关联对象技术的核心对象有
- AssociationsManager
- AssociationsHashMap
- ObjectAssociationMap
- ObjcAssociation
1.AssociationsManager的结构源码如下:
spinlock_t AssociationsManagerLock;
class AssociationsManager {
using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
static Storage _mapStorage;
public:
AssociationsManager() { AssociationsManagerLock.lock(); }
~AssociationsManager() { AssociationsManagerLock.unlock(); }
AssociationsHashMap &get() {
return _mapStorage.get();
}
static void init() {
_mapStorage.init();
}
};
主要作用:
- 它维护了 spinlock_t 和 AssociationsHashMap 的单例,初始化它的时候会调用 lock() 方法,在析构时会调用unlock(),而 get 方法用于取得一个全局的 AssociationsHashMap单例。
- 简单来说:AssociationsManager 通过持有一个自旋锁 spinlock_t 保证对 AssociationsHashMap 的操作是线程安全的,即每次只会有一个线程对 AssociationsHashMap 进行操作。
2.AssociationsHashMap的结构:
HashMap相当于OC中的NSDictionary。AssociationsHashMap的定义如下:
class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
AssociationsHashMap继承自unordered_map,使用的是C++语法。它的作用就是保存从对象的disguised_ptr_t到ObjectAssociationMap的映射。 我们可以理解为AssociationsHashMap以key-value的形式存着若干个ObjectAssociationMap。
2.ObjectAssociationMap:
class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator> {
public:
void *operator new(size_t n) { return ::malloc(n); }
void operator delete(void *ptr) { ::free(ptr); }
};
ObjectAssociationMap保存了从key到ObjcAssociation的映射,我们可以理解为ObjectAssociationMap以key-value的形式存着若干个ObjcAssociation对象。这个数据结构保存了当前对象对应的所有关联对象;
3.ObjcAssociation: ObjcAssociation的定义如下:
class ObjcAssociation {
uintptr_t _policy;
id _value;
public:
ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
ObjcAssociation() : _policy(0), _value(nil) {}
uintptr_t policy() const { return _policy; }
id value() const { return _value; }
bool hasValue() { return _value != nil; }
};
ObjcAssociation对象保存了对象对应的关联对象,其中的_policy和_value字段存的便是我们使用objc_setAssociatedObject方法时传入的policy和value。
整体关系如下:
总结:
- 关联对象并不是存储在被关联对象本身内存中,而是存储在全局的统一的一个AssociationsManager中
- AssociationsHashMap是全局唯一的,有AssociationsManager管理。
- ObjectAssociationMap以key为健存储关联对象的数据结构(ObjcAssociation对象),每一个对象都对应着一个ObjectAssociationMap,而ObjectAssociationMap则装着添加的关联对象,通过key-value一一对应。
- 分类通过关联对象添加属性实际只是帮它实现了set和get方法,并不是增加了一个属性。