首发于我的个人博客
分类的基本使用
- 首先我们定义一个类 YZPerson 继承自 NSObject
1 | @interface YZPerson : NSObject |
- 然后定义一个分类 YZPerson+test1.h
1 |
|
- 在控制器 ViewController 中使用
1 | - (void)viewDidLoad { |
- 执行结果为
CateogryDemo[23773:321096] -[YZPerson(test1) run]
注意点:如果原来的类和分类中有同样的方法,那么执行的结果的是分类中的,例如
1 |
|
- 执行结果不会发生变化,依然是
CateogryDemo[23773:321096] -[YZPerson(test1) run]
原因在后面分析
分类的结构
打开终端,进入项目下,执行如下命令,生成C语言的文件
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc YZPerson+test1.m
生成 YZPerson+test1.cpp文件
摘取主要代码如下
1 |
|
说明编译完之后每一个分类都会生成一个
_category_t
的结构体,里面有名称,对象方法列表,类方法列表,协议方法列表,属性列表,如果对应的为空,比如协议为空,属性为空,那么结构体中保存的就是0。
objc-runtime-new.h
打开源码最新的源码 runtime源码看,objc-runtime-new.h中分类结构体是这样的
1 | struct category_t { |
源码分析
源码解读顺序
1 | objc-os.mm |
- 先找到 objc-os.mm 类,里面的
1 |
|
- 继续跟下去
1 | map_images(unsigned count, const char * const paths[], |
- 查看
1 | map_images_nolock |
找到
1 | if (hCount > 0) { |
到了文件 objc-runtime-new.mm 中
1 | void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses) |
1 | static void remethodizeClass(Class cls) |
主要代码和注释已经在代码中展示了
1 | static void |
memmove memcpy
上面的代码继续跟下去来到了
1 | void attachLists(List* const * addedLists, uint32_t addedCount) { |
其中关键代码是
1 | // 内存挪动 |
关于memcpy与memmove的区别,可以参考 memcpy与memmove的区别
简单总结就是:
区别就在于关键字restrict, memcpy假定两块内存区域没有数据重叠,而memmove没有这个前提条件。如果复制的两个区域存在重叠时使用memcpy,其结果是不可预知的,有可能成功也有可能失败的,所以如果使用了memcpy,程序员自身必须确保两块内存没有重叠部分
总结
合并分类的时候,其方法列表等,不会覆盖掉原来类中的方法,是共存的。但是分类中的方法在前面,原来的类中的方法在后面,调用的时候,就会调用分类中的方法,如果多个分类有同样的方法,后编译的分类会调用。
问题
Category的使用场合是什么?
- 不同模块的功能区分开来,可以使用分类实现
Category的实现原理
- Category编译之后的底层结构是struct category_t,里面存储着分类的对象方法、类方法、属性、协议信息
在程序运行的时候,runtime会将Category的数据,合并到类信息中(类对象、元类对象中)
Category和Class Extension的区别是什么?
Class Extension在编译的时候,它的数据就已经包含在类信息中
Category是在运行时,才会将数据合并到类信息中
Category中有load方法吗?load方法是什么时候调用的?load 方法能继承吗?
有load方法
load方法在runtime加载类、分类的时候调用
load方法可以继承,但是一般情况下不会主动去调用load方法,都是让系统自动调用
Category能否添加成员变量?如果可以,如何给Category添加成员变量?
- 不能直接给Category添加成员变量,但是可以间接实现Category有成员变量的效果,关联对象
本文相关代码github地址 github
本文参考资料: