分类 - 476139183/Learning-iOS GitHub Wiki

https://www.jianshu.com/p/bfa2253d6b04

实现原理

是 运行时 动态合并。

分类的底层 数据结构(编译时):

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 在程序运行过程中只调用一次 调用顺序:

  1. 先调用类的+load
  • 按照编译先后顺序调用(先编译、先调用)
  • 调用子类的+load之前,会先调用父类的+load
  1. 再调用分类的+load
  • 按照编译的先后顺序调用先编译、先调用)

load 函数是当类或分类(Category)被加载到 Objective-C runtime 时(就是被引用的时候)被调用的,实现这个方法可以让我们在类加载的时候执行一些类相关的行为。当类被引用进项目的时候就会执行 load 函数(在 main 函数开始执行之前),与这个类是否被用到无关,每个类的 load 函数只会自动调用一次。load 函数调用特点如下:https://www.jianshu.com/p/a89743c186b8 和编译顺序有关

cls-data()->flags 标志是否加载过

load方法 存在继承关系的,因为手动调用其实就是走了 objc_msgSend 方法了

##load 和 initialize 的区别是什么,他们在分类中的调用顺序?,以及出现继承时,他们之间的调用过程 ##

分类能否添加成员变量?如何给分类添加成员变量

加载过程

  1. 通过 runtime 加载某一个类的所有 Category 数据
  2. 把 所有的 Category 的方法 属性 协议 等数据 合并到一个大数组。
  • 后面参与编译的 Category 数据,会在数组的前面
  1. 将合并后的分类数据(方法、属性、协议),插入到类元类的数组的前面

remethodizeClass(cls);
remethodizeClass(cls->ISA());

attachCategories(cls, cats, true /*flush caches*/); rw->methods.attachLists(mlists,mcount);

+ initialize 方法

  • initialize 会在类第一次接收到消息时调用

  • 调用顺序

    1. 先调用父类的 + initialize ,再调用子类的 + initialize
    2. 先初始化父类,再初始化子类,每一个类之后初始化一次

调用多次的原因是,因为子类没有实现,第一次调用是父类使用,第二次调用是子类方法寻找到父类调用。

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方法)