美文网首页iOS开发iOS 开发
做题系列之5---category

做题系列之5---category

作者: 08878f627e79 | 来源:发表于2016-04-01 16:17 被阅读81次

    题:
    ClassA:

    @interface ClassA : NSObject
    @end
    
    @implementation ClassA
    
    @end
    

    NSObject+cate:

    @interface NSObject (cate)
    @end
    
    @implementation NSObject (cate)
    - (void)test
    {
        NSLog(@"test Category");
    }
    @end
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
            [ClassA test]; 
        }
        return 0;
    }
    

    警告?报错?正常运行的输出
    老规矩,先看clang后的代码
    首先,

    // @interface NSObject (cate)
    /* @end */
    

    是的没错,我们import的头文件编译之后被干掉了。。。
    所以无论category声明中添加了什么函数,都没卵用。全让编译器干掉了。

    继续往下看

    // @implementation NSObject (cate)
    static void _I_NSObject_cate_test(NSObject * self, SEL _cmd) {
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_vb_2zmknph106j18bb6sbds_qlr0000gn_T_NSObject_cate_148908_mi_0);
    }
    // @end
    
    static struct _category_t _OBJC_$_CATEGORY_NSObject_$_cate __attribute__ ((used, section ("__DATA,__objc_const"))) = 
    {
     "NSObject",
     0, // &OBJC_CLASS_$_NSObject,
     (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_NSObject_$_cate,
     0,
     0,
     0,
    };
    
    static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
     &_OBJC_$_CATEGORY_NSObject_$_cate,
    };
    
    • category编译后的名字_category_t OBJC$CATEGORY_NSObject$_cate是按照规则生成的字符串,也就是说呢,category不能重名
    • 最下面的指针数组,保存的是category的指针,并把它存在了Data数据块中的__objc_catlist中。

    再看一下category的定义

    struct category_t {
        const char *name;
        classref_t cls;
        struct method_list_t *instanceMethods;
        struct method_list_t *classMethods;
        struct protocol_list_t *protocols;
        struct property_list_t *instanceProperties;
    };
    
    • name是class name,非category name
    • cls是扩展的类对象,运行时赋值
    • instanceProperties表示Category里所有的properties

    请比对结构体声明和上面的OBJC$CATEGORY_NSObject$_cate,对号入座。

    顺便再来看一下category的方法是如何被添加到方法列表的

    for (EACH_HEADER) {
        category_t **catlist = 
            _getObjc2CategoryList(hi, &count);
        for (i = 0; i < count; i++) {
            category_t *cat = catlist[i];
            Class cls = remapClass(cat->cls);
    
            BOOL classExists = NO;
            if (cat->instanceMethods ||  cat->protocols  
                ||  cat->instanceProperties) 
            {
                addUnattachedCategoryForClass(cat, cls, hi);
                if (cls->isRealized()) {
                    remethodizeClass(cls);
                    classExists = YES;
                }
            }
    
            if (cat->classMethods  ||  cat->protocols) 
            {
                addUnattachedCategoryForClass(cat, cls->ISA(), hi);
                if (cls->ISA()->isRealized()) {
                    remethodizeClass(cls->ISA());
                }
            }
        }
    }
    

    上一期介绍为什么类方法写在元类中的时候,引用了这段代码,这里再引用一下。
    这里首先调用_getObjc2CategoryList获取category_t**。_getObjc2CategoryList的实现如下:

    GETSECT(_getObjc2CategoryList,        category_t *,    "__objc_catlist");
    

    __objc_catlist就是之前写在Data数据块中的内容。
    接下来调用remethodizeClass,在remethodizeClass中调用attachCategoryMethods。

    static void 
    attachCategoryMethods(Class cls, category_list *cats, bool flushCaches)
    {
        if (!cats) return;
        if (PrintReplacedMethods) printReplacements(cls, cats);
    
        bool isMeta = cls->isMetaClass();
        method_list_t **mlists = (method_list_t **)
            _malloc_internal(cats->count * sizeof(*mlists));
    
        // Count backwards through cats to get newest categories first
        int mcount = 0;
        int i = cats->count;
        BOOL fromBundle = NO;
        while (i--) {
            method_list_t *mlist = cat_method_list(cats->list[i].cat, isMeta);
            if (mlist) {
                mlists[mcount++] = mlist;
                fromBundle |= cats->list[i].fromBundle;
            }
        }
    
        attachMethodLists(cls, mlists, mcount, NO, fromBundle, flushCaches);
    
        _free_internal(mlists);
    }
    

    这里将category_list取出后生成mlist放入到mlists中,注意是倒序放入哦!

    到此,相信你已经对问题的答案有所了解了。(答案是输出 test Category)

    相关文章

      网友评论

        本文标题:做题系列之5---category

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