Objective-C中的meta-class

讨论Objective-C的一个奇怪的概念 meta-class

在Objective-C中的每个类,都有它自己相关的meta-class,但因为你很少直接使用meta-class,所以显得很神秘。

在运行时建立一个类

下面的代码在运行时创建一个NSError新的子类,并添加一个方法到里面:

Class newClass =

objc_allocateClassPair([NSError class], “RuntimeErrorSubclass”, 0);

class_addMethod(newClass, @selector(report), (IMP)ReportFunction, “v@:”);

objc_registerClassPair(newClass);

添加的这个方法用ReportFunction函数名作为它的实现,实现定义在下面

void ReportFunction(id self, SEL _cmd)

{

NSLog(@”This object is %p.”, self);

NSLog(@”Class is %@, and super is %@.”, [self class], [self superclass]);

Class currentClass = [self class];

for (int i = 1; i < 5; i++)

{

NSLog(@”Following the isa pointer %d times gives %p”, i, currentClass);

currentClass = object_getClass(currentClass);

}

NSLog(@”NSObject’s class is %p”, [NSObject class]);

NSLog(@”NSObject’s meta class is %p”, object_getClass([NSObject class]));

}

表面上,这都很简单。在运行时创建一个新类,只需要3步

1)为 class pair分配存储空间 (使用objc_allocateClassPair)

2)增加需要的方法和ivars(使用class_addMethod来添加方法)

3) 注册这个类,以便它能被别人使用(objc_registerClassPair)

现在的问题是,什么是class pair, 函数objc_allocateClassPair只返回一个值:the class

那么pair的另外一半在哪里呢?你可能已经猜到另外一般就是meta-class(也就是本文的主题)

一个数据结构需要哪些东西才能成为一个对象

每个对象都有一个类,这是一个基本的面向对象的概念。

在Objective-C中,任何数据结构,如果在正确的位置有一个指向类的指针,就能被视为一个对象。

在Objective-C中,一个对象的类,由它的isa指针决定。这个isa指针指向 对象的类。

事实上,一个对象的基本定义是这样的:

typedef struct objc_object {

Class isa;

} *id;

这就是说,任何以一个指向Class结构的指针开始的结构,都能被当作objc_object

对象最重要的特性,就是你可以给它们发送消息:

[@"stringValue"

writeToFile:@"/file.txt" atomically:YES encoding:NSUTF8StringEncoding error:NULL];

当你发送消息给一个Objective-C对象时(比如这里的NSCFString), 运行时(runtime) 通过对象的isa指针得到对象的Class(这里是NSCFString类),而Class里含有那些可以应用这个类的所有对象上的所有方法的列表,以及指向superclass的指针。运行时通过类的方法列表和超类,来发现一个能同消息选择子匹配的方法(上面的例子中,就是在NSString类中的writeToFile:atomically:encoding:error方法)。

要点就是:类定义了那些消息,你只能发送那些已经定义好的消息给它的对象

什么是meta-class

现在,你可能已经知道,在Objective-C中,一个类也是一个对象。这意味着,你也可以发送消息给一个类

NSStringEncoding defaultStringEncoding = [NSString defaultStringEncoding];

在这种情况下, defaultStringEncoding被发送给NSString类

在Objective-C 中,每个类,都是一个对象。也就是说,类结构也必须以isa指针开始,这样,它才同objc_object结构二进制兼容

在结构里的第2个项目,必须是superclass的指针(如果是基类,没有父类的话,设置为nil)

定义一个类,有很多不同的方式,依赖于你的运行时版本而不同,但他们都以 isa开始,然后后面接着superclass

typedef struct objc_class *Class;

struct objc_class {

Class isa;

Class super_class;

/* followed by runtime specific details… */

};

为了让我们调用类的一个方法,类的isa指针必须指向一个类结构,并且,类结构必须含有我们能在该类上调用的方法列表

这就导致了一个meta-class的定义:meta-class是一个类对象的类

简单地说,

当你发送一条消息给一个对象时,这条消息会在对象的类的方法列表里查找

当你发送一条消息给一个类时,就会在类的meta-class的方法列表理查找消息

meta-class是必不可少的,因为它存储了一个类的类 方法。每个类都必须只有唯一的meta-class,因为每个类都只可能有一个唯一的类方法列表。

meta-class的类又是什么呢?

meta-class,跟 类一样,它也是一个对象。这意味着,你也可以在它上面调用方法。自然地,这意味这,它也必须有一个类。

所有的meta-class都使用基类的meta-class(在它们的继承体系中,最顶层的类的meta-class)作为它们自己的类。这意味着,所有从NSObject继承来的类,它们的meta-class都将NSObject的meta-class作为自己的类

遵循这个规则,所有的meta-class使用基类的meta-class作为它们自己的类,任何base meta-class都将是它自己的类(它们的isa指针指向它们自己)。也就是说,在 NSObject的meta-class的isa指针将指向它自己(它是自己的一个实例)

类和 meta-class的继承

同样的方式,类用super_class 指针指向超类,meta-class使用它自己的super_class指向 类的super-class的meta-class

巧合地是,基类的meta-class设置它的 super_class 为基类自己。

用实验来验证我们的想法

为了确认这些情况,我们看看ReportFunctional的输出。 这个函数的目的是 追踪isa指针,并记录在哪里找到的它。

为了运行ReportFunction,我们需要建立这个动态创建的类的实例,然后调用它的report方法

id instanceOfNewClass =

[[newClass alloc] initWithDomain:@”someDomain” code:0 userInfo:nil];

[instanceOfNewClass performSelector:@selector(report)];

[instanceOfNewClass release];

因为没有report方法的声明,我使用performSelector:来调用它,所以编译不会给出什么警告

ReportFunction将遍历isa指针,告诉我们那些对象被当成类,meta-class,以及meta-class的类 来使用

取得一个对象的类:ReportFunction将使用object_getClass来追踪isa指针, 因为isa指针是类的一个被保护的成员(你不能直接访问其他类的isa指针)

ReportFunction不使用类方法来实现这个,因为调用一个类对象的类方法,将不会返回meta-class. 而是再次返回这个类(所以[NSString class]将返回NSString类,而不是NSString的meta-class)

结论:

meta-class是类对象的类。每个类都有它自己唯一的meta-class(因为每个类都有它自己唯一的方法列表)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容