1、Category加载过程本质
在编译的时候,Category会被编译成叫做_Category_t的结构体,里面存储了属性列表、协议列表、对象方法、类方法列表。在程序运行时,runtime会将Category的数据合并到类对象和元类对象中。
- 运行时通过Runtime加载某一个类的所有Category数据
- 把所有Category的方法、属性、协议数据合并到一个大数组中,后面参加编译的Category数据会在数组的最前面
- 将合并后的Category数据,插入到类原来数据的前面
所以Category的方法属性信息还是存在类对象中;只是它们是在运行时加入类的;方法的调用还是会通过isa指针去class对象、mate_class对象中查找。
2、多个Category实现同一个方法,会调用哪一个Category的方法
会调用最后编译的Category的方法;因为在多个Category信息合并到类或者元类中,是以倒序方式合并进
去的,当调用方法的时候 就会首先找到最后编译的Category方法。
3、 Category和类扩展class Extension的区别
Class extension的内容是编译时就决议的,它的数据就包含在类信息中;
Category在程序运行时,runtime会将Category的数据合并到类对象和元类对象中;
4、Category里面有load方法吗? +load方法什么时候调用? load方法可以继承吗?
Load方法的调用是通过函数指针直接拿到方法进行直接调用的,并不是通过objc_msgSend()调用
肯定是有load方法的;
Load方法会在runtime 加载 类、分类的时候调用 ;而且只执行一次
Load方法的调用顺序不受分类load方法的影响,在低层call_class_loads方法中始终最先调用类的load方法,分类的load方法是先编译先调用
Load方法可以被继承,但是不会主动调用,load方法一般是系统调用
Load用途:加载的时候想做什么事情写在load方法里面
4、initialize方法什么时候调用
Initialize会在类第一次接收到消息(第一次初始化)的时候调用;主要是因为objc_msgSend()内部源码实现会去检查是否初始化,没有就调用Initalize,而且是先调用父类的initalize,再子类。
调用顺序:先调用父类在调用子类的initialize
initialize用途:第一次使用该类想做什么事情写在里面
5、load和initialize的区别,他们在Category中的调用顺序有什么不一样,以及出现继承他们的调用过程
- 调用方式:load是通过函数地址调用,initialize是通过objc_msgsend()
- 调用时机不一样:load是runtime加载类、分类的时候调用;initialize是第一次收到消息的时候调用,只执行一次(但是它子类没有实现会被多次调用)
- Load是先编译先调用,先调用父类再子类,Category也是先编译先调用;initialize先初始化父类再初始化子类
6、 分类里面可以添加成员变量吗?怎么实现添加属性?
Category的数据结构不能存放成员变量
可以添加属性,但只产生了set、get方法的声明,并没有生成成员变量和set、get方法的实现;
- 可以用runtime的关联属性的方法给Category添加属性,
- 也可以手动实现set、get方法,并在set方法进行属性的存储(可以用字典存储),然后get从存储的字典里面取出。 这种方法存在线程安全、
7、关联对象 - 分类添加属性
底层实现实际就是字典里面再套了一层字典。
- 关联对象并不是存储在被关联对象的内存中
- 关联对象存放在存储在全局统一的associationManager中
- 设置关联对象为nil,相当于移除此关联对象
- 移除全部的关联对象调用objc_removeAssociatedObjects()