OC的分类方法调用原理
在使用OC开发中,我们经常使用分类为一些不方便修改的类,添加分类,达到为类添加"属性"和方法的目的,下面是为LBPerson类添加分类的代码:
#import "LBPerson.h" NS_ASSUME_NONNULL_BEGIN @interface LBPerson (Animation) - (void)run; @end NS_ASSUME_NONNULL_END #import "LBPerson+Animation.h" @implementation LBPerson (Animation) - (void)run { NSLog(@"LBPerson+Animation.h"); } @end
如果分类中的方法和原类中方法名称相同,会优先调用分类中的方法。
LBPerson中代码如下:
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface LBPerson : NSObject - (void)run; @end NS_ASSUME_NONNULL_END
#import "LBPerson.h" @implementation LBPerson - (void)run { NSLog(@"LBPerson run"); } @end
LBPerson+Animation中代码如下:
#import "LBPerson.h" NS_ASSUME_NONNULL_BEGIN @interface LBPerson (Animation) - (void)run; @end NS_ASSUME_NONNULL_END
#import "LBPerson+Animation.h" @implementation LBPerson (Animation) - (void)run { NSLog(@"Animation run"); } @end
在main方法中调用person对象的run方法代码以及打印如下:
int main(int argc, const char * argv[]) { @autoreleasepool { LBPerson *person = [[LBPerson alloc] init]; [person run]; } return 0; } // 输出结果 Animation run
如果两个分类中含有相同的方法,则会按照编译的顺序,编译靠后的分类会被调用
下面就从runtime的源码分析为什么会按照上述规则进行调用:
// cls 本类 // cats_list 存储分类数组 // cats_count 存储分类的数量 static void attachCategories(Class cls, const locstamped_category_t *cats_list, uint32_t cats_count, int flags) { if (slowpath(PrintReplacedMethods)) { printReplacements(cls, cats_list, cats_count); } if (slowpath(PrintConnecting)) { _objc_inform("CLASS: attaching %d categories to%s class '%s'%s", cats_count, (flags & ATTACH_EXISTING) ? " existing" : "", cls->nameForLogging(), (flags & ATTACH_METACLASS) ? " (meta)" : ""); } /* * Only a few classes have more than 64 categories during launch. 在启动期间只有少数类拥有超过64个分类 * This uses a little stack, and avoids malloc. * 使用少量栈避免分类 * Categories must be added in the proper order, which is back 分类肯定会按照合适的顺序被添加,这个顺序就是从后向前 * to front. To do that with the chunking, we iterate cats_list 为了做这个组块,我们迭代分类数组从前到后 * from front to back, build up the local buffers backwards, 建造一个向后的本地缓冲 * and call attachLists on the chunks. attachLists prepends the 调用附加数组在多个组块上 * lists, so the final result is in the expected order. 附加数组频道list上,所以最终的结果就是按照期待的顺序 */ constexpr uint32_t ATTACH_BUFSIZ = 64; method_list_t *mlists[ATTACH_BUFSIZ]; // 类方法数组 property_list_t *proplists[ATTACH_BUFSIZ]; // 对象方法数组 protocol_list_t *protolists[ATTACH_BUFSIZ]; //协议数组 uint32_t mcount = 0; uint32_t propcount = 0; uint32_t protocount = 0; bool fromBundle = NO; bool isMeta = (flags & ATTACH_METACLASS); // 是否是元类 auto rwe = cls->data()->extAllocIfNeeded(); // 找到本类的rw 并且判断是否需要额外分配 // 遍历分类数组 for (uint32_t i = 0; i < cats_count; i++) { auto& entry = cats_list[i]; // 具体的分类 // 取出类方法 method_list_t *mlist = entry.cat->methodsForMeta(isMeta); if (mlist) { if (mcount == ATTACH_BUFSIZ) { prepareMethodLists(cls, mlists, mcount, NO, fromBundle); // 添加到本来的方法列表中 rwe->methods.attachLists(mlists, mcount); mcount = 0; } mlists[ATTACH_BUFSIZ - ++mcount] = mlist; fromBundle |= entry.hi->isBundle(); } // 取出对象方法 property_list_t *proplist = entry.cat->propertiesForMeta(isMeta, entry.hi); if (proplist) { if (propcount == ATTACH_BUFSIZ) { rwe->properties.attachLists(proplists, propcount); propcount = 0; } proplists[ATTACH_BUFSIZ - ++propcount] = proplist; } // 取出协议 protocol_list_t *protolist = entry.cat->protocolsForMeta(isMeta); if (protolist) { if (protocount == ATTACH_BUFSIZ) { rwe->protocols.attachLists(protolists, protocount); protocount = 0; } protolists[ATTACH_BUFSIZ - ++protocount] = protolist; } } if (mcount > 0) { prepareMethodLists(cls, mlists + ATTACH_BUFSIZ - mcount, mcount, NO, fromBundle); rwe->methods.attachLists(mlists + ATTACH_BUFSIZ - mcount, mcount); if (flags & ATTACH_EXISTING) flushCaches(cls); } rwe->properties.attachLists(proplists + ATTACH_BUFSIZ - propcount, propcount); rwe->protocols.attachLists(protolists + ATTACH_BUFSIZ - protocount, protocount); }
决定方法调用顺序就是下面源码:
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])); } }
上述源码注释纯属个人的理解