小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
Category的使用场合是什么?
Category的实现原理?
Category和Extension的区别是什么?
Category中有load方法吗?load方法是什么时候调用的?
load方法能继承吗?
load、initialize方法的区别是什么?它们在category中的
调用的顺序?以及出现继承时他们之间的调用过程?
Category能否添加成员变量?如果可以,如何给Category添加成员变量?
创建一个Person类,创建Person的Eat分类,Person的Test分类。
@interface Person : NSObject
- (void)run;
@end
@implementation Person
- (void)eat
{
NSLog(@"eat");
}
@end
@interface Person (Eat) <NSCopying, NSCoding>
- (void)eat;
@property (assign, nonatomic) int weight;
@property (assign, nonatomic) double height;
@end
@implementation Person (Eat)
- (void)eat
{
NSLog(@"eat");
}
@end
@interface Person (Test)
- (void)test;
@end
@implementation Person (Test)
- (void)test
{
NSLog(@"test");
}
@end
Person *person = [[Person alloc] init];
[person run];
[person test];
[person eat];
我们所说的调用方法,就是发送消息。
person 调用run方法,就是
objc_msgSend(person, @selector(run));
前边我们说过isa及对象实例方法在类对象中。可以很清楚的知道run方法,在person对象的类对象的方法列表中。。。
那 test方法 eat方法是分类中的方法 , 它们存到什么地方了呢?
难道分类也有类对象????我们分类的对象方法,放在对应的分类类对象里吗?
答案是不存在的。 一个类永远只有一个类对象。不存在分类类对象。
那 test方法 eat方法 最终存放在什么哪里呢?
那到底放在那里呢??????
其实,最终程序运行起来之后,run方法 test方法 eat方法,1个分类也好100个分类也好,最终都会合并到
一个类对象中。存在到类对象的方法列表中。。。。也就是Person类对象的方法列表里。。。。。。
不论你调用那个方法,最终都是通过instance对象的isa到类对象中寻找,然后调用的。。。。。。这是一个结论,稍后证明。
那么分类中的类方法存在在什么地方呢?同样,不管一个分类也好,100个分类也好,其中的类方法,最终都会合并到一个元类对象中,存到元类的类对象的方法列表中。这是结论,稍后证明。
那什么时候合并分类中的方法到类中呢?在编译的时候?还是在运行的时候呢?
是在运行的时候,会用到runtime,在程序运行过程中合并的。
接下来==========
我们看看编译后的分类的本质是什么?
找到Person+Test.m所在的文件夹。
在终端敲入如下指令:xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc Person+Test.m
查看生成的Person+Test.cpp文件
struct _category_t {
const char *name;// 分类名字
struct _class_t *cls; // 类
const struct _method_list_t *instance_methods;// 分类中的实例方法列表
const struct _method_list_t *class_methods;// 分类中的类方法列表
const struct _protocol_list_t *protocols;
// 分类也可以遵守协议,分类的协议列表
const struct _prop_list_t *properties;
// 分类中的 属性列表(分类中的属性只是一个 方法声明,没有方法实现。也不生成成员变量)
};
找到 分类编译完毕后 的 底层结构体。
程序没运行之前,你的分类方法是存在在这个分类的结构体中。
当程序运行时,利用runtime把分类结构体中的方法列表中的方法,合并到类对象的方法列表中。
我们再找到这个编译后的代码:
static struct _category_t _OBJC_$_CATEGORY_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) =
{
"Person",
0, // &OBJC_CLASS_$_Person,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test,
0,
0,
};
再给分类结构体赋值。
我们看到给分类的 五个域赋值。
我们看第三个域 (const struct _method_list_t *)&OBJC_CATEGORY_INSTANCE_METHODS_Person__Test,是什么东东???
我们找的编译后的源代码是这样的:
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_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"test", "v16@0:8", (void *)_I_Person_Test_test}}
};
有没有看到一个"test" ,没错,这正是我们的test方法。