分类 - 476139183/Learning-iOS GitHub Wiki
实现原理
是 运行时 动态合并。
分类的底层 数据结构(编译时):
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;
};
"Person"
0
实例方法列表
类方法列表
协议列表
属性列表
然后进行赋值(objc_inithooks):
static void OBJC_CATEGORY_SETUP_$_MJPerson_$_Eat(void ) {
_OBJC_$_CATEGORY_MJPerson_$_Eat.cls = &OBJC_CLASS_$_MJPerson;
}
Category 编译之后的底层结构是 _category_t。 里面存储着分类的对象方法,类方法,属性,协议信息 在程序运行时,runtime 会将Category的数据 合并到类信息中(类对象,元类对象)
分类和扩展的区别是什么
class Extension 在编译时,它的数据已经包含在类信息里中 Category在运行时,才会将数据合并在类信息中
分类中有没有load方法么,load方法是什么时候调用的?load方法能继承么
load 是在加载类和分类的时候调用,
每一个类、分类的+load 在程序运行过程中只调用一次 调用顺序:
- 先调用类的+load
- 按照编译先后顺序调用(先编译、先调用)
- 调用子类的+load之前,会先调用父类的+load
- 再调用分类的+load
- 按照编译的先后顺序调用先编译、先调用)
load 函数是当类或分类(Category)被加载到 Objective-C runtime 时(就是被引用的时候)被调用的,实现这个方法可以让我们在类加载的时候执行一些类相关的行为。当类被引用进项目的时候就会执行 load 函数(在 main 函数开始执行之前),与这个类是否被用到无关,每个类的 load 函数只会自动调用一次。load 函数调用特点如下:https://www.jianshu.com/p/a89743c186b8 和编译顺序有关

cls-data()->flags 标志是否加载过
load方法 存在继承关系的,因为手动调用其实就是走了 objc_msgSend 方法了
##load 和 initialize 的区别是什么,他们在分类中的调用顺序?,以及出现继承时,他们之间的调用过程 ##
分类能否添加成员变量?如何给分类添加成员变量
加载过程
- 通过 runtime 加载某一个类的所有 Category 数据
- 把 所有的 Category 的方法 属性 协议 等数据 合并到一个大数组。
- 后面参与编译的 Category 数据,会在数组的前面
- 将合并后的分类数据(方法、属性、协议),插入到类元类的数组的前面

remethodizeClass(cls);
remethodizeClass(cls->ISA());
attachCategories(cls, cats, true /*flush caches*/);rw->methods.attachLists(mlists,mcount);
+ initialize 方法
-
initialize 会在类第一次接收到消息时调用
-
调用顺序
- 先调用父类的 + initialize ,再调用子类的 + initialize
- 先初始化父类,再初始化子类,每一个类之后初始化一次

调用多次的原因是,因为子类没有实现,第一次调用是父类使用,第二次调用是子类方法寻找到父类调用。
void _class_initialize(Class cls)
{
assert(!cls->isMetaClass());
Class supercls;
bool reallyInitialize = NO;
// Make sure super is done initializing BEFORE beginning to initialize cls.
// See note about deadlock above.
supercls = cls->superclass;
if (supercls && !supercls->isInitialized()) {
_class_initialize(supercls);
}
// Try to atomically set CLS_INITIALIZING.
{
monitor_locker_t lock(classInitLock);
if (!cls->isInitialized() && !cls->isInitializing()) {
cls->setInitializing();
reallyInitialize = YES;
}
}
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方法)