Category的底层结构 分类底层结构定义的如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 //objc-runtime-new.h 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); };
项目中每定义一个分类,底层都会增加一个category_t对象。
Category的加载过程 Category源码阅读顺序:
objc-os.mm (runtime入口)
_objc_init (runtime初始化)
map_images
map_images_nolock
objc-runtime-new.mm
_read_images
remethodizeClass
attachCategories
attachLists
realloc、memmove、 memcpy
category的加载过程:
通过runtime加载类的所有分类
将所有分类的方法,属性,协议分别合并到一个数组
将合并后的分类数据插入到类原来到数据之前
由源码可见,对同名方法而言,会优先调用分类中的方法。如果多个分类中包含同名方法,则会调用最后参与编译的分类中的方法。
摘录源码中核心的attachCategories实现如下(objc4-756.2):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 // Attach method lists and properties and protocols from categories to a class. // Assumes the categories in cats are all loaded and sorted by load order, // oldest categories first. 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_t,method_t],[method_t,method_t,method_t]] //二维数组中的一个元素(数组)存放一个分类中的方法列表 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; } } //取出(元)类对象中的数据(class_rw_t) 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); }
Category和Extension的区别
类扩展中的信息在编译时会合并到类信息中。
分类中的信息在运行时才会合并到类信息中。