Category 底层分析
栏目: Objective-C · 发布时间: 6年前
内容简介:一、Category 浅层分析参照上图,思考关于分类的问题。分类有没有可能同上图的 class 或 meta-class 处于并列关系,每创建一个分类分别对应着一个 分类class 和 分类meta-class 对象?
-
一、Category 浅层分析
-
二、Category 底层结构
-
三、Category 源码分析(分类方法优先调用)
-
四、小结
一、Category 浅层分析
参照上图,思考关于分类的问题。分类有没有可能同上图的 class 或 meta-class 处于并列关系,每创建一个分类分别对应着一个 分类class 和 分类meta-class 对象?
这里先说出结论,在第二小节再做验证。实际情况并非如此,为了充分利用资源,一个类永远只存在一个类对象。Category 中的对象方法会合并到类(class)的对象方法列表中,类方法合并到元类(meta-class)的类方法列表中。方法合并的时机在运行时进行,而非编译期间。
二、Category 底层结构
@implementation Person - (void)run{ NSLog(@"run"); } @end @implementation Person (Test) - (void)test{ NSLog(@"test"); } + (void)test{ } @end @implementation Person (Eat) - (void)eat{ NSLog(@"eat"); } + (void)eat{ } @end
为验证上述问题,可以编写如上代码,即创建 Person 以及 Person + Test 和 Person + Eat 分类。然后借助xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc OC源文件 -o 输出的CPP文件 命令将 Person + Test类转为 C/C++ 代码。
生成的 .cpp 文件中包含 _category_t 结构体,该结构体即为分类底层数据结构,主要包含类名、对象方法、类方法、协议以及属性。
struct _category_t { const char *name;//类名,这里是Person struct _class_t *cls; const struct _method_list_t *instance_methods;//对象方法列表 const struct _method_list_t *class_methods;//类方法列表 const struct _protocol_list_t *protocols;//协议列表 const struct _prop_list_t *properties;//属性列表 };
生成的.cpp 文件中还存在下下面一段代码,该段代码与 _category_t结构体对应,依次给_category_t 结构体内部成员赋值。其中第二、五、六三个参数均为 0。
static struct _category_t _OBJC_$_CATEGORY_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = { "Person", 0, // &OBJC_CLASS_$_Person, (const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test, (const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test, 0, 0, };
从上述验证可以看出,编译期间过后,每个分类唯一对应一个 _category_t 结构,与原有类是分开的。
三、Category 源码分析(分类方法优先调用)
为进一步理解 Category ,可以查看在该网站 查看 objc4-723 runtime 底层源码 。可以顺着如下顺序阅读源码,其中 objc-os.mm 文件为运行时的入口文件。
objc-os.mm ---> _objc_init ---> map_images ---> map_images_nolock --->objc-runtime-new.mm --->_read_images --->remethodizeClass --->attachCategories --->attachLists
在_read_images方法中可以发现这样一段代码,代码上方注释为 Discover categories , 另外还有个二维数组category_t ** catlist, catlist 数组中元素为结构体,存储着一堆 category_t 结构。remethodizeClass 方法被调用两次,从命名来看意思为:重新方法化,两次传入参数分别为 cls 和 cls->ISA, 即类对象的元类对象。
// Discover categories. for (EACH_HEADER) { category_t **catlist = _getObjc2CategoryList(hi, &count); bool hasClassProperties = hi->info()->hasCategoryClassProperties(); for (i = 0; i < count; i++) { category_t *cat = catlist[I]; Class cls = remapClass(cat->cls); if (!cls) { // Category's target class is missing (probably weak-linked). // Disavow any knowledge of this category. catlist[i] = nil; if (PrintConnecting) { _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with " "missing weak-linked target class", cat->name, cat); } continue; } // Process this category. // First, register the category with its target class. // Then, rebuild the class's method lists (etc) if // the class is realized. bool classExists = NO; if (cat->instanceMethods || cat->protocols || cat->instanceProperties) { addUnattachedCategoryForClass(cat, cls, hi); if (cls->isRealized()) { remethodizeClass(cls); classExists = YES; } if (PrintConnecting) { _objc_inform("CLASS: found category -%s(%s) %s", cls->nameForLogging(), cat->name, classExists ? "on existing class" : ""); } } if (cat->classMethods || cat->protocols || (hasClassProperties && cat->_classProperties)) { addUnattachedCategoryForClass(cat, cls->ISA(), hi); if (cls->ISA()->isRealized()) { remethodizeClass(cls->ISA()); } if (PrintConnecting) { _objc_inform("CLASS: found category +%s(%s)", cls->nameForLogging(), cat->name); } } } }
remethodizeClass 方法内部调用了 attachCategories 方法,attachCategories 方法前两个参数分别为 class 和 分类数组cats。
static void remethodizeClass(Class cls) { category_list *cats; bool isMeta; runtimeLock.assertWriting(); isMeta = cls->isMetaClass(); // 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); } }
attachCategories 方法内部创建了三个二维数组method_list_t **mlists,property_list_t **proplists,protocol_list_t **protolists, 分别用于保存方法、属性和协议。mlists[mcount++] = mlist; 表示取出每个分类中的方法列表放入到二维数组中;auto rw = cls->data(); 表示取出类对象中的数据;rw-
>methods.attachLists(mlists, mcount);表示将所有分类的对象方法附加到类对象方法列表中。 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_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); }
attachLists 方法中调用memmove方法将类中的原有方法方法放到数组末尾,调用memcpy方法将二维数组 addedLists 中的每个 Category 的方法列表放置到类中原有方法的前面。
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])); } }
四、小结
经过上述源码分析,最终方法在内存中的布局结构如下。该布局很好说明了分类方法调用顺序要优于原有类方法。因为调用方法时,会顺序遍历二维数组查找方法,当查找到目标方法后就无需再向后继续遍历查找方法。
作者:ZhengYaWei
链接:https://www.jianshu.com/p/bdf200c28752
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- RabbitMq底层原理分析
- 数据分析的三大框架:底层技术、分析建模、工具选择
- Colly源码解析——结合例子分析底层实现
- 面试必问:HashMap 底层实现原理分析
- 直面底层:“吹上天”的协程,带你深入源码分析
- 「Go」- golang源码分析 - channel的底层实现
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
An Introduction to Genetic Algorithms
Melanie Mitchell / MIT Press / 1998-2-6 / USD 45.00
Genetic algorithms have been used in science and engineering as adaptive algorithms for solving practical problems and as computational models of natural evolutionary systems. This brief, accessible i......一起来看看 《An Introduction to Genetic Algorithms》 这本书的介绍吧!
在线进制转换器
各进制数互转换器
RGB CMYK 转换工具
RGB CMYK 互转工具