Runtime

Runtime - Category

2021-07-26  本文已影响0人  ienos
struct category_t {
    // 名字
    const char *name;
    // class
    classref_t cls;
    // 实例方法,cls 为 class
    struct method_list_t *instanceMethods;
    // 类方法,cls 为 meta
    struct method_list_t *classMethods;
    // 协议
    struct protocol_list_t *protocols;
    // 实例属性,cls 为 class
    struct property_list_t *instanceProperties;
};

思考

源码阅读

_read_images
1. addUnattachedCategories: map class & category
/* objc-runtime-new.mm */
void _read_images(header_info **hList, uint32_t hCount)
{

    ...

    category_t **catlist = _getObjc2CategoryList(hi, &count);

    for (i = 0; i < count; i++) {
        category_t *cat = catlist[i];
        Class cls = remapClass(cat->cls);
        ...
        // Process this category. 
        bool classExists = NO;
        if (cat->instanceMethods ||  cat->protocols  
            ||  cat->instanceProperties) 
        {
            addUnattachedCategoryForClass(cat, cls, hi);
            if (cls->isRealized()) {
               remethodizeClass(cls);
               classExists = YES;
            }
              ...
         }
    }
    ...
}
static void addUnattachedCategoryForClass(category_t *cat, Class cls, 
                                          header_info *catHeader)
{
    runtimeLock.assertWriting();

    NXMapTable *cats = unattachedCategories();
    category_list *list;

    list = (category_list *)NXMapGet(cats, cls);    
    if (!list) {
        list = (category_list *)
            calloc(sizeof(*list) + sizeof(list->list[0]), 1);
    } else {
        list = (category_list *)
            realloc(list, sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
    }
    list->list[list->count++] = (locstamped_category_t){cat, catHeader};
    
    NXMapInsert(cats, cls, list);
}

NXMAPTable 保存一个 Key 为 Class,Value 为 category_list

2. unattachedCategory 中的 unattached 是什么意思? 什么时候 attached ?
static Class remethodizeClass(Class cls)
{
    ...
    // Re-methodizing: check for more categories
    if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
        if (PrintConnecting) {
            _objc_inform("CLASS: attaching categories to class '%s' %s", 
                         cls->nameForLogging(), isMeta ? "(meta)" : "");
        }
        
        attachCategories(cls, cats, true /*flush caches*/);        
        free(cats);
    }
}

remethodizeClass_read_images 中有调用,说明需要在 class realized 后才能调用 attachedCategory

    if (cls->isRealized()) {
        remethodizeClass(cls);
        classExists = YES;
    }
3. attachedCategory: category 中的方法、属性、协议添加到 class 中
static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    if (!cats) return;
    if (PrintReplacedMethods) printReplacements(cls, cats);

    bool isMeta = cls->isMetaClass();

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

    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
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            mlists[mcount++] = mlist;
            fromBundle |= entry.hi->isBundle();
        }

        /// property_list
        property_list_t *proplist = entry.cat->propertiesForMeta(isMeta);
        if (proplist) {
            proplists[propcount++] = proplist;
        }

        /// protocol_list
        protocol_list_t *protolist = entry.cat->protocols;
        if (protolist) {
            protolists[protocount++] = protolist;
        }
    }

    auto rw = cls->data();

    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    // rw->methods 添加 method_list
    rw->methods.attachLists(mlists, mcount);
    free(mlists);
    if (flush_caches  &&  mcount > 0) flushCaches(cls);

    // rw->properties 添加 property_list
    rw->properties.attachLists(proplists, propcount);
    free(proplists);

    // rw->protocols 添加 protocols_list
    rw->protocols.attachLists(protolists, protocount);
    free(protolists);
}
4. category 重载 class 方法

例如重载 Company 中的 setValue:forKey

    var count: UInt32 = 0
    guard let methods = class_copyMethodList(Company.self, &count) else { return }
    for index in 0..<count {
        let sel = method_getName(methods[Int(index)])
        print(String.fromCString(cs: sel_getName(sel), length: 30))
    }

结果发现有两个同名的 SEL,setValue:forKey

sel_getName
上一篇 下一篇

猜你喜欢

热点阅读