Category详解-本质分析


前言

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加载处理过程

  1. 通过runtime加载某个类的所有category数据
    比如MJPerson有2个分类(MJPerson+Eat) (MJPerson+Test)
  2. 把所有category的方法、属性、协议数据,合并到一个大数组中
    后面参与编译的Category数据,会在数组的前面
    例如,先编译的MJPerson+Eat,后面编译的MJPerson+Test,通过源码的分析就能看到MJPerson+Test,会放到数组的前面
  3. 将合并的分类数据 (方法、属性、协议)插入到类原来数据的前面
    例如:当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方法,只是调用顺序不同

  1. 先调用类的+load,也就是先调用MJPerson的+load,如果此时还有其它的分类MJTeacher,会按照编译的先后顺序调用
  2. 调用完类的了,在调子类MJStudent的+load
  3. 当子类调用完了以后,才开始调用分类的+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方法
请添加图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值