美文网首页Runtime
Runtime - Category

Runtime - Category

作者: ienos | 来源:发表于2021-07-26 22:48 被阅读0次
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;
};
  • category 支持协议

思考

  • Category 什么时候加载属性和方法?
  • Category 怎么加载属性和方法?
  • Category 可以加载成员变量吗?
  • Category 和 Class 的关系如何表示?
  • Category 重载 Class 中的方法?

源码阅读

_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;
            }
              ...
         }
    }
    ...
}
  • _read_images 中先调用 _getObjc2CategoryList 获取 category_list
  • 再遍历 category_list 调用 addUnattachedCategoryForClass 关联 class 和 category
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);
}
  • prepareMethodLists 方法待研究?
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

相关文章

  • iOS 的Category实现原理

    Category 加载过程原理 是通过runtime加载类的所有Category数据 把Category的方法、属...

  • Objective-C:Category

    Category的底层结构 Category的加载处理过程 通过Runtime加载某个类的所有Category数据...

  • OC分类

    Category底层结构 Category加载过程 1.通过Runtime加载某个类的所有Category数据 2...

  • category探究

    category本质 category attach2Class objc-os.mm objc-runtime-...

  • iOS底层:Category

    Category的底层结构 Category的加载处理过程1、通过Runtime加载某个类的所有Category数...

  • iOS Category(分类)

    Category的加载处理过程 通过runtime加载某个类的所有category数据。 把所有的category...

  • Category - runtime

    @porperty 做了以下三件事 1.生成实例变量_property2.生成getter方法- property...

  • Runtime - Category

    category 支持协议 思考 Category 什么时候加载属性和方法? Category 怎么加载属性和方法...

  • Objective-C 之 Category

    Category 是基于 Objective-C runtime 的一种体现。 Category 原理首先,这里先...

  • 结合 category 工作原理分析 OC2.0 中的 runt

    结合 category 工作原理分析 OC2.0 中的 runtime 结合 category 工作原理分析 OC...

网友评论

    本文标题:Runtime - Category

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