Category(分类)方法调用

70 阅读2分钟

一个类的两个分类有同名方法,每次调用这个方法时,方法响应的实现是否一致。

多个分类中的同名方法只会调用一个,具体调用哪个方法和编译顺序有关,具体是后编译分类的方法会覆盖先编译的同名方法,可以通过在Xcode中调整编译顺序进行调整。
修改位置是Build Phases下的Compile Sources

有没有办法让所有分类的方法都调用一次?

如果两个分类的方法同名,且都与原来类的某个方法进行方法交换,则交换两次相当于没交换,因为同名方法的实现只会去找后编译的那个分类找实现。
分类A实现了wp_viewWillAppear:方法且与原类的viewWillAppear:方法交换
分类B实现了wp_viewWillAppear:方法且与原类的viewWillAppear:方法交换
本质上是wp_viewWillAppear:和viewWillAppear:交换了两次,相当于没交换
如果两个分类的方法不同名,且都与原来类的方法进行交换,则执行顺序是倒叙执行文件的方法。2->1->宿主类方法。

分类的数据结构和方法查找原理

编译完成后,每一个分类都会生成一个结构体category_t,里面存储着分类的对象方法、类方法、属性、协议信息。
程序运行的时候,Runtime会将Category的数据合并到类信息中,由于分类方法会合并到类信息中,所以同名方法只会找到一个实现。

// 分类结构体
struct category_t {
    const char *name; // 分类名
    classref_t cls; // 类名
    struct method_list_t *instanceMethods; // 实例方法列表
    struct method_list_t *classMehtods; // 类方法列表
    struct protocol_list_t *protocols; // 协议方法列表
    struct property_list_t *instanceProperties;// 实例属性列表
    struct property_list_t *_classProperties; // 类属性列表
    
    method_list_t *methodForMeta(bool isMeta) {
        if(isMeta) return classMethods;
        else return instanceMethods;
    }
    
    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
}

// 方法列表结构体
struct method_list_t {
    uint32_t entsizeAndFlags;
    uint32_t count; // 方法列表中有几个方法
    struct method_t methods[1]; // 类型为method_t的数组,存储具体方法结构体
}

struct method_t {
    SEL name; // 选择子(Selector),方法名,比如mtMethod
    const char *types; // 方法签名,标识方法返回值和参数
    IMP imp; // 方法的实现指针,指向myMethod的方法实现
}

方法交换,实际上是交换了方法结构体method_t中的imp指针。