Category本质

939 阅读3分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。


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方法。