Object-C中Category

作者: zwwuchn | 来源:发表于2019-08-01 14:50 被阅读14次

    什么是Category

    Objective-C类别也叫分类,是一种不需要继承即可给类扩充一些方法的语法技术。

    Category使用

    @interface LPPerson : NSObject
    -(void)test;
    @end
    @implementation LPPerson
    - (void)test {
        NSLog(@"LPPerson--test");
    }
    @end
    
    @interface LPPerson (Extension)
    - (void)test1;
    + (void)test2;
    - @end
    - @implementation LPPerson (Extension)
    - (void)test1 {
        NSLog(@"LPPerson (Extension)--test1");
    }
    + (void)test2 {
        NSLog(@"LPPerson (Extension)--+test2");
    }
    @end
    
    @interface LPPerson (Eat)
    - (void)eat1;
    + (void)eat2;
    @end
    @implementation LPPerson (Eat)
    - (void)eat1 {
        NSLog(@"LPPerson (Eat)--eat1");
    }
    + (void)eat2 {
        NSLog(@"LPPerson (Eat)--+eat2");
    }
    @end
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
           
            LPPerson *person = [[LPPerson alloc] init];
            
            [person test];
            
            [person test1];
            [person eat1];
            
            [LPPerson test2];
            [LPPerson eat2];
            
        }
        return 0;
    }
    打印:
    // LPPerson--test1
    // LPPerson (Extension)--test1
    // LPPerson (Eat)--eat1
    // LPPerson (Extension)--+test2
    // LPPerson (Eat)--+eat2
    

    Category真面目

    我们来将OC代码转换为C/C++代码或者查看runtime开源代码

    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 文件名 -o 输出的CPP文件
    struct _category_t {
        const char *name; //1
        struct _class_t *cls; //2
        const struct _method_list_t *instance_methods; //3
        const struct _method_list_t *class_methods; //4
        const struct _protocol_list_t *protocols; //5
        const struct _prop_list_t *properties; //6
    };
    
    编译后生成的代码结构
    static struct _category_t _OBJC_$_CATEGORY_LPPerson_$_Extension __attribute__ ((used, section ("__DATA,__objc_const"))) = 
    {
        "LPPerson",
        0, // &OBJC_CLASS_$_LPPerson,
        (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_LPPerson_$_Extension,
        (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_LPPerson_$_Extension,
        0,
        0,
    };
    
    图片.png
    这个category所有的-方法
    static struct /*_method_list_t*/ {
        unsigned int entsize;  // sizeof(struct _objc_method)
        unsigned int method_count;
        struct _objc_method method_list[1];
    } _OBJC_$_CATEGORY_INSTANCE_METHODS_LPPerson_$_Extension __attribute__ ((used, section ("__DATA,__objc_const"))) = {
        sizeof(_objc_method),
        1,
        {{(struct objc_selector *)"test1", "v16@0:8", (void *)_I_LPPerson_Extension_test1}}
    };
    
    这个category所有的+方法
    static struct /*_method_list_t*/ {
        unsigned int entsize;  // sizeof(struct _objc_method)
        unsigned int method_count;
        struct _objc_method method_list[1];
    } _OBJC_$_CATEGORY_CLASS_METHODS_LPPerson_$_Extension __attribute__ ((used, section ("__DATA,__objc_const"))) = {
        sizeof(_objc_method),
        1,
        {{(struct objc_selector *)"test2", "v16@0:8", (void *)_C_LPPerson_Extension_test2}}
    };
    
    图片.png

    最后所有的对象方法放到类对象中,类方法放在元类对象中

    Category本质

    图片.png
    static void 
    attachCategories(Class cls, category_list *cats, bool flush_caches)
    {
        if (!cats) return;
        if (PrintReplacedMethods) printReplacements(cls, cats);
    
        bool isMeta = cls->isMetaClass();
    
        // fixme rearrange to remove these intermediate allocations
        // 方法数组
        method_list_t **mlists = (method_list_t **)
            malloc(cats->count * sizeof(*mlists));
        // 属性数组
        property_list_t **proplists = (property_list_t **)
            malloc(cats->count * sizeof(*proplists));
        // 协议数组
        protocol_list_t **protolists = (protocol_list_t **)
            malloc(cats->count * sizeof(*protolists));
    
        // Count backwards through cats to get newest categories first
        int mcount = 0;
        int propcount = 0;
        int protocount = 0;
        int i = cats->count;
        bool fromBundle = NO;
        while (i--) {
            // 取出某个分类
            auto& entry = cats->list[i];
            // 拿到分类的对象方法、类方法列表(根据isMeta判断对应的方法)
            method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
            if (mlist) {
                mlists[mcount++] = mlist;
                fromBundle |= entry.hi->isBundle();
            }
    
            property_list_t *proplist = 
                entry.cat->propertiesForMeta(isMeta, entry.hi);
            if (proplist) {
                proplists[propcount++] = proplist;
            }
    
            protocol_list_t *protolist = entry.cat->protocols;
            if (protolist) {
                protolists[protocount++] = protolist;
            }
        }
        // 通过rw得到类对象里面的数据类型
        auto rw = cls->data();
    
        prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
        // 将所有分类的对象方法,附加到类对象的方法列表中
        rw->methods.attachLists(mlists, mcount);
        free(mlists);
        if (flush_caches  &&  mcount > 0) flushCaches(cls);
        // 将所有分类的属性,附加到类对象的属性列表中
        rw->properties.attachLists(proplists, propcount);
        free(proplists);
        // 将所有分类的协议,附加到类对象的协议列表中
        rw->protocols.attachLists(protolists, protocount);
        free(protolists);
    }
    
    重点看attachLists方法
    void attachLists(List* const * addedLists, uint32_t addedCount) {
        if (addedCount == 0) return;
    
        if (hasArray()) {
            // many lists -> many lists
            uint32_t oldCount = array()->count;
            // 原来方法列表数量 + 新增加方法列表数量
            uint32_t newCount = oldCount + addedCount;
            // 重新分配分类方法列表数组内存结构
            setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
            array()->count = newCount;
            // array()->lists: 原来的方法列表向右挪动addedCount个位置
            memmove(array()->lists + addedCount, array()->lists, 
                    oldCount * sizeof(array()->lists[0]));
            // addedLists: 所有分类方法列表(数组)
            // 将新添加的方法列表,拷贝到原来的方法列表(所以会拷贝在原来方法列表的前面->最后参与编译先调用)
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
        else if (!list  &&  addedCount == 1) {
            // 0 lists -> 1 list
            list = addedLists[0];
        } 
        else {
            // 1 list -> many lists
            List* oldList = list;
            uint32_t oldCount = oldList ? 1 : 0;
            uint32_t newCount = oldCount + addedCount;
            setArray((array_t *)malloc(array_t::byteSize(newCount)));
            array()->count = newCount;
            if (oldList) array()->lists[addedCount] = oldList;
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }
    

    总结

    • Category实现原理

      • Category编译后生成结构struct category_t,里面存放分类的对象方法,类方法,属性,协议信息。在程序运行的时候,runtime会将Category中的数据,合并到类信息中(类对象,元类对象中)
    • Category和Class Extension的区别

      • Class Extension在编译的时候,它的数据就已经包含在类信息中
      • Category是在运行时,才会将数据合并到类信息中

    相关文章

      网友评论

        本文标题:Object-C中Category

        本文链接:https://www.haomeiwen.com/subject/fmgqdctx.html