分类中添加方法的实现原理

2019-10-12  本文已影响0人  初灬终

方法调用

image.png

源码(主干部分)

static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    bool isMeta = cls->isMetaClass();
    method_list_t **mlists = (method_list_t **) malloc(cats->count * sizeof(*mlists));

    int mcount = 0;
    int i = cats->count;
    bool fromBundle = NO;

    //反向遍历,将分类的方法添加到mlist数组。
    while (i--) {
        auto& entry = cats->list[i];

        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;
        }
    }
    //获取class的相关信息,其中包含方法列表
    auto rw = cls->data();
    
    //在class的方法列表上,拼接上分类的方法列表
    //调用该方法后,分类方法才真正添加到class的方法列表中。
    rw->methods.attachLists(mlists, mcount);
}
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,迭代器里的List **lists,表示未拼接分类方法的二维数组。
        //addedCount是一个uint32_t(4Byte),array()->lists是指针(4Byte)的数组
        //sizeof(array()->lists[0])  = sizeof(指针)
        memmove(array()->lists + addedCount, array()->lists, 
                oldCount * sizeof(array()->lists[0]));
        
        //内存复制。将新增的分类方法,拷贝到类的方法列表中,从首地址开始。
        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;
        //将class的原有方法,移动到分类方法之后。
        if (oldList) array()->lists[addedCount] = oldList;
        memcpy(array()->lists, addedLists, 
               addedCount * sizeof(array()->lists[0]));
    }
}

总结

0.分类的方法,是在编译的时候添加的,还是运行时添加?

运行时

1.为什么分类方法会覆盖掉原有方法?
memmove(array()->lists + addedCount, array()->lists, 
                oldCount * sizeof(array()->lists[0]));
memcpy(array()->lists, addedLists, 
               addedCount * sizeof(array()->lists[0]));

因为分类的方法,在方法列表中的位置,在原有方法之前。实际上原有方法依旧存在,没有被真正的覆盖。

2.为什么分类中的同名方法,最后编译的分类生效?

因为分类方法是反向遍历的,最后编译的方法,它在方法列表中的位置越靠前。实际上所有的同名方法都存在。

上一篇下一篇

猜你喜欢

热点阅读