void_read_images(header_info**hList,uint32_thCount){..._free_internal(resolvedFutureClasses);}// Discover categories. for(EACH_HEADER){category_t**catlist=_getObjc2CategoryList(hi,&count);for(i=0;i<count;i++){category_t*cat=catlist[i];Classcls=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. BOOLclassExists=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/* || 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);}}}}// Category discovery MUST BE LAST to avoid potential races // when other threads call the new category code before // this thread finishes its fixups.// +load handled by prepare_load_methods()...}
staticvoidattachMethodLists(Classcls,method_list_t**addedLists,intaddedCount,boolbaseMethods,boolmethodsFromBundle,boolflushCaches){...newLists[newCount++]=mlist;}// Copy old methods to the method list arrayfor(i=0;i<oldCount;i++){newLists[newCount++]=oldLists[i];}if(oldLists&&oldLists!=oldBuf)free(oldLists);// nil-terminatenewLists[newCount]=nil;if(newCount>1){assert(newLists!=newBuf);cls->data()->method_lists=newLists;cls->setInfo(RW_METHOD_ARRAY);}else{assert(newLists==newBuf);cls->data()->method_list=newLists[0];assert(!(cls->data()->flags&RW_METHOD_ARRAY));}}
// class's method list is an array of method lists#define RW_METHOD_ARRAY (1<<20)union{method_list_t**method_lists;// RW_METHOD_ARRAY == 1method_list_t*method_list;// RW_METHOD_ARRAY == 0};
看过我上一篇博文《Objective-C +load vs +initialize》的朋友可能已经有所察觉了。我们注意到 runtime 对 Category 中方法的处理过程并没有对 +load 方法进行什么特殊地处理。因此,严格意义上讲 Category 中的 +load 方法跟普通方法一样也会对主类中的 +load 方法造成覆盖,只不过 runtime 在自动调用主类和 Category 中的 +load 方法时是直接使用各自方法的指针进行调用的。所以才会使我们觉得主类和 Category 中的 +load 方法好像互不影响一样。因此,当我们手动给主类发送 +load 消息时,调用的一直会是分类中的 +load 方法,you should give it a try yourself 。
总结
Category 是 Objective-C 中非常强大的技术之一,使用得当的话可以给我们的开发带来极大的便利。很多著名的开源库或多或少都会通过给系统类添加 Category 的方式提供强大功能,比如 AFNetworking 、ReactiveCocoa 、 SDWebImage 等。但是凡事有利必有弊,正因为 Category 非常强大,所以一旦误用就很可能会造成非常严重的后果。比如覆写系统类的方法,这是 iOS 开发新手经常会犯的一个错误,不管在任何情况下,切记一定不要这么做,No zuo no die 。