美文网首页
category底层

category底层

作者: 有毒的程序猿 | 来源:发表于2018-12-25 14:38 被阅读17次
1.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;
    // Fields below this point are not always present on disk.
    struct property_list_t *_classProperties;

    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    } // 获取方法列表

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
  • 每一个分类的结构都是这样的
2.category加载过程
  • 通过Runtime_objc_init加载某个类的所有Category数据.
  • 把所有Category的方法、属性、协议数据,合并到一个大数组中,
    后面参与编译的Category数据,会在数组的前面.
  • 将合并后的分类数据(方法、属性、协议),插入到类原来数据的前面.
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];

        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;
        }
    }

    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);
}

  • 分类方法合并过程大概如下:


    类和分类的方法.jpg
原类和分类的方法列表指向一个类似数组的结构,里面放着一个指针,指向它的方法列表.
  • 首先原类会调用
 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;
            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;
            if (oldList) array()->lists[addedCount] = oldList;
            memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
        }
    }

这两个方法是关键
 memmove(array()->lists + addedCount, array()->lists, 
                    oldCount * sizeof(array()->lists[0]));
 memcpy(array()->lists, addedLists, 
                   addedCount * sizeof(array()->lists[0]));
  • 内存移动memmove有几个分类移动几个
原类内存移动.jpeg
  • 内存复制memcpy后编译的分类在前面
内存复制.jpg

通过objc_msgSend()调用方法时,分类会覆盖原类的方法.显而易见,从方法列表里会先找到分类的方法.

相关文章

网友评论

      本文标题:category底层

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