一、Category的声明
Category的主要作用是为已经存在的类添加方法。
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;
// Fields below this point are not always present on disk.
struct property_list_t *_classProperties;
};
Category主要包含实例方法、类方法、遵循的协议和属性,主要是为了扩充类而设计的。
二、Category的生成
首先来定义一个分类
@interface TestCategoryClass(Category)
- (void)categoryMethod;
-@property(nonatomic) NSString *categoryProperty;
@end
@implementation TestCategoryClass(Category)
- (void)categroyMethod{}
- (void)setCategoryProperty:(NSString *)categoryProperty{}
- (NSString *)categoryProperty{return nil;}
@end
接下来用clang重写该文件,来分析下重写后的cpp文件:
1. 生成方法列表
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[3];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_TestCategoryClass_$_Category __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
3,
{{(struct objc_selector *)"categoryMethod", "v16@0:8", (void *)_I_TestCategoryClass_Category_categoryMethod},
{(struct objc_selector *)"setCategoryProperty:", "v24@0:8@16", (void *)_I_TestCategoryClass_Category_setCategoryProperty_},
{(struct objc_selector *)"categoryProperty", "@16@0:8", (void *)_I_TestCategoryClass_Category_categoryProperty}}
};
分类TestCategoryClass(Category)共三个方法,分别是categoryMethod、setCategoryProperty:和categoryProperty。
2.生成属性
static struct /*_prop_list_t*/ {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_TestCategoryClass_$_Category __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"categoryProperty","T@\"NSString\",N"}}
};
分类中的property只会声明setter和getter,不会生成setter和getter。
3.分类初始化
_OBJC_$_CATEGORY_TestCategoryClass_$_Category __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"TestCategoryClass",
0, // &OBJC_CLASS_$_TestCategoryClass,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_TestCategoryClass_$_Category,
0,
0,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_TestCategoryClass_$_Category,
};
这里进行的工作是初始化category_t结构体,将属性、方法和协议等填充进去,并将分类的cls指向对应的类。
三、Category的加载
1._read_images
Category的加载主要在_read_images中,接下来看下_read_images中的实现:
map_images()
->map_images_no_lock()
->_read_images()
void _read_images(header_info **hList, uint32_t hCount, int totalClasses, int unoptimizedTotalClasses{
// other
for(EACH_HEADER){
category_t **catlist = _getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassproperties();
for(int 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 || (hasClassProperties && cat->_classProperties)){
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if(cls->ISA()->isRealized()){
remethodizeClass(cls->ISA());
}
}
}
}
}
_read_images主要的工作就是将category的方法和属性通过remethodizeClass方法添加在class上,如果是类方法的话则添加到元类上。
addUnattachedCategoryForClass的作用是将category添加到NXMap中,以方便之后取出来使用。
2.attachCategories
接下来看一下remethodizeClass的实现,remethodizeClass主要是调用了attachCategories将cls和category连接起来:
void remethodizeClass(Class cls){
...
attachCategories(cls, cats, true);
}
void attachCategories(Class cls, category_list *cats, bool flush_caches){
method_list_t **mlists;
property_list_t **proplists;
protocols_list_t ** protolists;
int i = cats->count;
while(i--){
auto& entry = cats->list[i];
mlists[mcount++] = entry.cat->methodsForMeta(isMeta);
proplists[propcount++] = entry.cat->methods->propertiesForMeta(isMeta, entry.hi);
protolists[protocount++] = entry.cat->protocols;
}
auto rw = cls->data();
rw->methods.attachLists(mlists, mcount);
rw->properties.attachLists(proplists, propcount);
rw->protocols.attachLists(protolists, protocount);
}
可以看出,attachCategories就是将方法、属性和协议添加到类cls中。这里要注意一下,attachLists会将新添加的list添加到前面去,这样Category如果有同名方法的话就会优先被调用到,也是为什么会覆盖类的同名方法的原因。
四、Category与Extension的区别
Extension(类扩展)是Category的一个特例,其名字为匿名(为空)。比如:
@interface TestCategoryClass()
@end
Extension在编译期决议,它就是类的一部分,在编译期和头文件里的@interface以及实现文件里的@implement一起形成一个完整的类,它伴随类的产生而产生,亦随之一起消亡。Extension一般用来隐藏类的私有信息,你必须有一个类的源码才能为一个类添加extension,所以你无法为系统的类比如NSString添加extension。
小结
- Category的主要作用是为已经存在的类添加方法。
- Category在编译期决议,然后在加载时将Category附加到对应类上。
- Category的方法会覆盖原有类的同名方法。
- Extension是类的一部分,能添加变量,必须有一个类的源码才能添加extension。
网友评论