什么是Category
Objective-C类别也叫分类,是一种不需要继承即可给类扩充一些方法的语法技术。
Category使用
@interface LPPerson : NSObject
-(void)test;
@end
@implementation LPPerson
- (void)test {
NSLog(@"LPPerson--test");
}
@end
@interface LPPerson (Extension)
- (void)test1;
+ (void)test2;
- @end
- @implementation LPPerson (Extension)
- (void)test1 {
NSLog(@"LPPerson (Extension)--test1");
}
+ (void)test2 {
NSLog(@"LPPerson (Extension)--+test2");
}
@end
@interface LPPerson (Eat)
- (void)eat1;
+ (void)eat2;
@end
@implementation LPPerson (Eat)
- (void)eat1 {
NSLog(@"LPPerson (Eat)--eat1");
}
+ (void)eat2 {
NSLog(@"LPPerson (Eat)--+eat2");
}
@end
int main(int argc, const char * argv[]) {
@autoreleasepool {
LPPerson *person = [[LPPerson alloc] init];
[person test];
[person test1];
[person eat1];
[LPPerson test2];
[LPPerson eat2];
}
return 0;
}
打印:
// LPPerson--test1
// LPPerson (Extension)--test1
// LPPerson (Eat)--eat1
// LPPerson (Extension)--+test2
// LPPerson (Eat)--+eat2
Category真面目
xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc 文件名 -o 输出的CPP文件
struct _category_t {
const char *name; //1
struct _class_t *cls; //2
const struct _method_list_t *instance_methods; //3
const struct _method_list_t *class_methods; //4
const struct _protocol_list_t *protocols; //5
const struct _prop_list_t *properties; //6
};
编译后生成的代码结构
static struct _category_t _OBJC_$_CATEGORY_LPPerson_$_Extension __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"LPPerson",
0, // &OBJC_CLASS_$_LPPerson,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_LPPerson_$_Extension,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_LPPerson_$_Extension,
0,
0,
};
图片.png
这个category所有的-方法
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_LPPerson_$_Extension __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"test1", "v16@0:8", (void *)_I_LPPerson_Extension_test1}}
};
这个category所有的+方法
static struct /*_method_list_t*/ {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_CLASS_METHODS_LPPerson_$_Extension __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"test2", "v16@0:8", (void *)_C_LPPerson_Extension_test2}}
};
图片.png
最后所有的对象方法放到类对象中,类方法放在元类对象中
Category本质
图片.png
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_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];
// 拿到分类的对象方法、类方法列表(根据isMeta判断对应的方法)
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;
}
}
// 通过rw得到类对象里面的数据类型
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方法
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;
// array()->lists: 原来的方法列表向右挪动addedCount个位置
memmove(array()->lists + addedCount, array()->lists,
oldCount * sizeof(array()->lists[0]));
// addedLists: 所有分类方法列表(数组)
// 将新添加的方法列表,拷贝到原来的方法列表(所以会拷贝在原来方法列表的前面->最后参与编译先调用)
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]));
}
}
总结
-
Category实现原理
-
Category编译后生成结构struct category_t,里面存放分类的对象方法,类方法,属性,协议信息。在程序运行的时候,runtime会将Category中的数据,合并到类信息中(类对象,元类对象中)
-
Category和Class Extension的区别
-
Class Extension在编译的时候,它的数据就已经包含在类信息中
-
Category是在运行时,才会将数据合并到类信息中
网友评论