Objective-C Runtimeの类与对象

Objective-C是一门动态语言,它将很多静态语言在编译和链接时期做的事放到了运行时来处理。这样处理也就意味着,它将使我们的代码更有灵活性。比如,我们可以根据我们的意向将消息转发给其它对象,或者去替换我们想要实现的方法等。因为Objective-C“动态化”的内容都是在运行时完成的,所以,OC的运行条件不仅仅要求有帮助我们向机器说话的编译器,还要有让代码随心而动的运行时,而这个运行时就是objc Runtime。它基本上是由C和汇编实现的,它让C具有了面向对象的能力

Runtime库主要做下面几件事:

  • 封装:在这个库中,对象可以用C语言中的结构体表示,而方法可以用C函数来实现,另外再加上了一些额外的特性。这些结构体和函数被runtime函数封装后,我们就可以在程序运行时创建,检查,修改类、对象和它们的方法了。
  • 找出方法的最终执行代码:当程序执行[object doSomething]时,会向消息接收者(object)发送一条消息(doSomething),runtime会根据消息接收者是否能响应该消息而做出不同的反应。

类与对象

Objective-C是对C的进一步封装,让C有了面对对象的能力,为什么这么说呢,我们可以看一下下面的这个例子:

Person *per = [[Person alloc] init];

这是一个很简单的获取实例化对象的方法。
那么这个语句在经过编译后会变成什么样呢,想看的话我们可以这样做:

  • 打开Xcode,创建一个命令行文件

  • 创建一个.m 文件,在里边定义我们想要的类, 内容如下所示:

#import <Foundation/Foundation.h>

@interface Person : NSObject

@property (nonatomic, assign) NSInteger age;

@end

@implementation Person

@end

int main(int argc, const char * argv[]) {
    @autoreleasepool {
        Person *p = [[Person alloc] init];
        p.age = 18;
        NSLog(@"%ld", p.age);
    }
    return 0;
}

  • 打开命令行,编译main.m为.cpp文件,打开文件可以看到main函数中的实现如下:
int main(int argc, const char * argv[]) {
    /* @autoreleasepool */ 
    { 
        __AtAutoreleasePool __autoreleasepool; 
        Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));
        ((void (*)(id, SEL, NSInteger))(void *)objc_msgSend)((id)p, sel_registerName("setAge:"), (NSInteger)18);
        NSLog((NSString *)&__NSConstantStringImpl__var_folders_dd_c58dp2w556l44jqlhxdgthdh0000gn_T_main_449954_mi_0, ((NSInteger (*)(id, SEL))(void *)objc_msgSend)((id)p, sel_registerName("age")));
    }
    return 0;
}

在以上函数中,采用了消息发送机制,可以看到,OC在这里也脱去了它面向对象的外衣,显露除了些许本质。
以实例化对象p的初始化过程为例,简要分析一下其实现过程:
首先,在原文件中的

Person *p = [[Person alloc]init];

先后调用了两个方法,也即发送了两个消息, 对应的,编译后文件就是实现这一过程。

Person *(*)(id, SEL)(void *)objc_msgSend((id)objc_getClass("Person"), sel_registerName("alloc"));

这一方法对应的就是Person调用的alloc方法,objc_msgSend函数发送alloc消息(需要强转),它将会返回一个结构体(Person的实例化对象)。同理,返回的对象在执行初始化方法init时,需要再次发送消息,也就有了在.cpp文件中我们看到的样子:

Person *p = ((Person *(*)(id, SEL))(void *)objc_msgSend)((id)((Person *(*)(id, SEL))(void *)objc_msgSend)((id)objc_getClass("Person"), sel_registerName("alloc")), sel_registerName("init"));

*注:在.m中如果也想如此实现,需要引用<objc/runtime.h>和<objc/message.h>


数据结构

Class

Objective-C类是由Class类型来表示的,它实际上是一个指向objc_class结构体的指针。
查看objc/runtime.h中objc_class结构体的定义如下:

struct objc_class {

    Class isa  OBJC_ISA_AVAILABILITY;



#if !__OBJC2__

    Class super_class                       OBJC2_UNAVAILABLE;  // 父类

    const char *name                        OBJC2_UNAVAILABLE;  // 类名

    long version                            OBJC2_UNAVAILABLE;  // 类的版本信息,默认为0

    long info                               OBJC2_UNAVAILABLE;  // 类信息,供运行期使用的一些位标识

    long instance_size                      OBJC2_UNAVAILABLE;  // 该类的实例变量大小

    struct objc_ivar_list *ivars            OBJC2_UNAVAILABLE;  // 该类的成员变量链表

    struct objc_method_list **methodLists   OBJC2_UNAVAILABLE;  // 方法定义的链表

    struct objc_cache *cache                OBJC2_UNAVAILABLE;  // 方法缓存

    struct objc_protocol_list *protocols    OBJC2_UNAVAILABLE;  // 协议链表

#endif



} OBJC2_UNAVAILABLE;

MetaClass

在objc_class的结构体中有isa这么一个字段,在Objective-C中,所有的类自身也是一个对象,这个对象的Class里面也有一个isa指针,它指向metaClass(元类)。
当我们向一个对象发送消息时,runtime会在这个对象所属的这个类的方法列表中查找方法;而向一个类发送消息时,会在这个类的meta-class的方法列表中查找。
meta-class之所以重要,是因为它存储着一个类的所有类方法。每个类都会有一个单独的meta-class,因为每个类的类方法基本不可能完全相同。
再深入一下,meta-class也是一个类,也可以向它发送一个消息,那么它的isa又是指向什么呢?为了不让这种结构无限延伸下去,Objective-C的设计者让所有的meta-class的isa指向基类的meta-class,以此作为它们的所属类。即,任何NSObject继承体系下的meta-class都使用NSObject的meta-class作为自己的所属类,而基类的meta-class的isa指针是指向它自己。这样就形成了一个完美的闭环。
通过上面的描述,再加上对objc_class结构体中super_class指针的分析,我们就可以描绘出类及相应meta-class类的一个继承体系了,如下图所示:

继承关系Class与Meta

我是这样去理解这个图的:
图中的各个类的关系以及isa指向就如同一个老亚家的家谱以及财产关系,首先图中的subClass可以看做是以诺, 他管理着一个村落中的所有兵器(instance of Class的方法),当村民需要使用时,需要问自己的村长有没有,并在有的情况下调用,如果没有呢?当你要去干掉一条龙,意气风发的去跟村长要剑时,发现他竟然没有,当时你就要崩溃,村长一看,“哎哟哟,你可别死我家门口,我问问我老爹有没有”,然后就给该隐打电话,向他借,再不行就让该隐向亚当借,这要是都没有。。以诺就会告诉村民“行了,大家一起死吧,你要的东西大家都没有”。然后全世界就崩溃掉了(野指针:指向了未识别的方法)。
这时候你就要问了,那subclass(meta)又是何方神圣呢?好吧,我也不知道她叫啥,是以诺的媳妇就是了,她藏着以诺的小金库,里边装着村长能用的兵器(类方法),当村长要用时就得向她申请,同理,她没有就得向她妈要,在这里因为女子无需上战场,所以她们本身要兵器。。你猜她们想干嘛,所以她们想要兵器直接向RootClass(meta)夏娃说句悄悄话, 夏娃想要兵器也没地要去,看看自己有啥吧。秉承着男传男,女传女的原则,可以很清楚的看清图中的继承关系。等等,天呐,RootClass(meta)继承于RootClass,这是咋回事?嘿嘿,别忘了,夏娃是用亚当的一根肋骨造出来的。而亚当,你能跟造出他那位打通电话吗?


类与对象的操作函数

Objective-C Runtime 运行时之一:类与对象

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

推荐阅读更多精彩内容