文章目录
前言
Category在系统编译的时候会把分类中属性,对象方法,类方法分别放到一个大数组中,此时属于2个独立的个体。
等程序运行的时候会运用系统runtime,进行合并,把对象方法,属性,协议信息放到类对象中,把类方法放到元类对象中
提示:以下是本篇文章正文内容,下面案例可供参考
Category的底层结构
1.底层结构
struct _category_t {
const char *name; //分类的名字
struct _class_t *cls;
const struct _method_list_t *instance_methods;//对象方法
const struct _method_list_t *class_methods;//类方法
const struct _protocol_list_t *protocols;//分类属性
const struct _prop_list_t *properties;//协议
};
2.Category加载处理过程
- 通过runtime加载某个类的所有category数据
比如MJPerson有2个分类(MJPerson+Eat) (MJPerson+Test) - 把所有category的方法、属性、协议数据,合并到一个大数组中
后面参与编译的Category数据,会在数组的前面
例如,先编译的MJPerson+Eat,后面编译的MJPerson+Test,通过源码的分析就能看到MJPerson+Test,会放到数组的前面 - 将合并的分类数据 (方法、属性、协议)插入到类原来数据的前面
例如:当MJPerson中有个run方法,MJPerson+Eat也有个run方法,MJPerson+Test中也有run方法,当你调用run方法的时候会先执行数组中的第一个,也就是MJPerson+Test中的run方法,其它的不执行
当分类的方法全部执行完毕以后,在执行MJPerson的方法
3. +load调用顺序
+load方法会在runtime加载类,分类的时候调用,每个类,分类的+load方法在程序运行过程中只调用一次
MJPerson 有2个分类(MJPerson+Eat)(MJPerson+Test)
MJStudent 继承MJPerson,中也有2个分类(MJStudent+Eat)(MJStudent+Test)
比如:MJPerson和MJStudent 还有4个子类都有+load方法,程序运行的时候,首先肯定会全部调用这些load方法,只是调用顺序不同
- 先调用类的+load,也就是先调用MJPerson的+load,如果此时还有其它的分类MJTeacher,会按照编译的先后顺序调用
- 调用完类的了,在调子类MJStudent的+load
- 当子类调用完了以后,才开始调用分类的+load,这个时候也会按照编译的顺序来调用,看先编译的谁,就先调用谁
先调用类的+load方法(根据编译的顺序)
在调用子类+load方法
在调用分类+load方法(根据编译顺序)
注意: +load方法是根据方法地址直接调用,并不是经过objc_msgSend函数调用的
这也就是+load方法不会被覆盖
4. +initialize的方法
- +initialize方法会在类第一次接收到消息的时候调用。
什么叫接收消息 ?
[MJPerson alloc] 它的本质就是
objc_msgSend([MJPerson class],@selector(‘alloc’)),这就是在给MJPerson发送了一个alloc方法,这个时候就会调用initialize
initialize调用顺序
- MJPerson 中有 + initialize,当调用[MJPerson alloc]的时候会触发一次 +initialize方法
- MJPerson+Test1,MJPerson+Test2,中也有+initialize,再次调用[MJPerson alloc],就会触发分类的+ initialize方法(后编译,先调用)
- MJStudent继承MJPerson,如果子类没有+ initialize,就会调用父类的+initialize,所以父类的+ initialize可能会被多次调用
5. +load 与+initialize区别
1.调用方式
1> load是根据函数地址直接调用
2> initialize是通过objc_msgSend调用
2.调用时刻
1> load是runtime加载类、分类的时候调用(只会调用1次)
2> initialize是类第一次接收到消息的时候调用,每一个类只会initialize一次(父类的initialize方法可能会被调用多次)
load、initialize的调用顺序?
1.load
1> 先调用类的load
a) 先编译的类,优先调用load
b) 调用子类的load之前,会先调用父类的load
2> 再调用分类的load
a) 先编译的分类,优先调用load
2.initialize
1> 先初始化父类
2> 再初始化子类(可能最终调用的是父类的initialize方法)
6.分类中声明属性
当你在分类中声明一个属性时候,会自动生成set方法和get方法,但是并不会去实现它!!!
分类不支持成员变量!!
可以这样声明一个全局变量,并且在+load方法中初始化
这样可以实现set get方法