题:
ClassA:
@interface ClassA : NSObject
@end
@implementation ClassA
@end
NSObject+cate:
@interface NSObject (cate)
@end
@implementation NSObject (cate)
- (void)test
{
NSLog(@"test Category");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
[ClassA test];
}
return 0;
}
警告?报错?正常运行的输出
老规矩,先看clang后的代码
首先,
// @interface NSObject (cate)
/* @end */
是的没错,我们import的头文件编译之后被干掉了。。。
所以无论category声明中添加了什么函数,都没卵用。全让编译器干掉了。
继续往下看
// @implementation NSObject (cate)
static void _I_NSObject_cate_test(NSObject * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_vb_2zmknph106j18bb6sbds_qlr0000gn_T_NSObject_cate_148908_mi_0);
}
// @end
static struct _category_t _OBJC_$_CATEGORY_NSObject_$_cate __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"NSObject",
0, // &OBJC_CLASS_$_NSObject,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_NSObject_$_cate,
0,
0,
0,
};
static struct _category_t *L_OBJC_LABEL_CATEGORY_$ [1] __attribute__((used, section ("__DATA, __objc_catlist,regular,no_dead_strip")))= {
&_OBJC_$_CATEGORY_NSObject_$_cate,
};
- category编译后的名字_category_t OBJC$CATEGORY_NSObject$_cate是按照规则生成的字符串,也就是说呢,category不能重名
- 最下面的指针数组,保存的是category的指针,并把它存在了Data数据块中的__objc_catlist中。
再看一下category的定义
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;
};
- name是class name,非category name
- cls是扩展的类对象,运行时赋值
- instanceProperties表示Category里所有的properties
请比对结构体声明和上面的OBJC$CATEGORY_NSObject$_cate,对号入座。
顺便再来看一下category的方法是如何被添加到方法列表的
for (EACH_HEADER) {
category_t **catlist =
_getObjc2CategoryList(hi, &count);
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
BOOL classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if (cls->isRealized()) {
remethodizeClass(cls);
classExists = YES;
}
}
if (cat->classMethods || cat->protocols)
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if (cls->ISA()->isRealized()) {
remethodizeClass(cls->ISA());
}
}
}
}
上一期介绍为什么类方法写在元类中的时候,引用了这段代码,这里再引用一下。
这里首先调用_getObjc2CategoryList获取category_t**。_getObjc2CategoryList的实现如下:
GETSECT(_getObjc2CategoryList, category_t *, "__objc_catlist");
__objc_catlist就是之前写在Data数据块中的内容。
接下来调用remethodizeClass,在remethodizeClass中调用attachCategoryMethods。
static void
attachCategoryMethods(Class cls, category_list *cats, bool flushCaches)
{
if (!cats) return;
if (PrintReplacedMethods) printReplacements(cls, cats);
bool isMeta = cls->isMetaClass();
method_list_t **mlists = (method_list_t **)
_malloc_internal(cats->count * sizeof(*mlists));
// Count backwards through cats to get newest categories first
int mcount = 0;
int i = cats->count;
BOOL fromBundle = NO;
while (i--) {
method_list_t *mlist = cat_method_list(cats->list[i].cat, isMeta);
if (mlist) {
mlists[mcount++] = mlist;
fromBundle |= cats->list[i].fromBundle;
}
}
attachMethodLists(cls, mlists, mcount, NO, fromBundle, flushCaches);
_free_internal(mlists);
}
这里将category_list取出后生成mlist放入到mlists中,注意是倒序放入哦!
到此,相信你已经对问题的答案有所了解了。(答案是输出 test Category)