Runtime学习的一点总结

首先从网上一段代码说起

.h文件
#import "father.h"

@interface Father : NSObject

@property(nonatomic,copy) NSString * name;

@end


@interface son : father

@end
 
---------------------------------------------------

.m文件
@implementation Father

- (instancetype)init {
    if (self == [super init]) {
        self.name = @"";
    }
    return self;
}

@end


@implementation Son

- (instancetype)init {
    if (self == [super init]) {
        NSLog(@"self class-->%@", [self class]);
        NSLog(@"super class-->%@", [super class]);
        
    }
    return self;
}

@end

执行Son *son = [[Son alloc] init];输出结果

2016-08-20 20:25:15.512 Test[792:28328] self class-->Son
2016-08-20 20:25:16.272 Test[792:28328] super class-->Son

对于第一段代码,我一开始以为super class打印出Father,self class打印出Son,结果输出确实2个Son,这是什么原因呢?
我们平时用OC调用对象方法如[target MethodName:var1],会被编译为objc_msgSend(target, @selector(MethodName:), var1)

ObjC 是一种面向runtime(运行时)的语言,也就是说,它会尽可能地把代码执行的决策从编译和链接的时候,推迟到运行时。这给程序员写代码带来很大的灵活性,比如说你可以把消息转发给你想要的对象,或者随意交换一个方法的实现之类的。这就要求 runtime 能检测一个对象是否能对一个方法进行响应,然后再把这个方法分发到对应的对象去。我们拿 C 来跟 ObjC 对比一下。在 C 语言里面,一切从 main 函数开始,程序员写代码的时候是自上而下地,一个 C 的结构体或者说类吧,是不能把方法调用转发给其他对象的。但是在oc中,我们可以在运行时把上面的target换成其他对象,非常灵活。

objc_msgSend函数的原型如下:

id objc_msgSend ( id self, SEL op, ... )

函数里2个参数
id:指向类实例的指针typedef struct objc_object *id;
其中objc_object 定义是

struct objc_object {
 Class isa;  // 指向对象所属类的指针 
}

通过isa指针并不总能获取到对象的类别,网上的解释如下

isa指针不总是指向实例对象所属的类,不能依靠它来确定类型,而是应该用class方法来确定实例对象的类。因为KVO的实现机理就是将被观察对象的isa指针指向一个中间类而不是真实的类,这是一种叫做 isa-swizzling 的技术。

SEL:objc_msgSend函数第二个参数类型为SEL,它是selector在Objc中的表示类型(Swift中是Selector类)。selector是方法选择器,可以理解为区分方法的 ID,而这个 ID 的数据结构是SEL:typedef struct objc_selector *SEL;

objc_msgSend的函数流程:
1、先检查是否忽略selector
2、检查target是否为nil,OC允许nil执行方法
3、然后查找在这个类的IMP查找,先从cache找,找到就跳到对应函数
4、cache则去方法分发表上查找
5、本类找不到,则去父类找,一直到NSObject为止
6、如果还找不到就要开始进入动态方法解析了(这个我不懂)
上图:

Paste_Image.png

isa指针

上面的objc_msgSend实现原理里面提到了isa指针、类
我们知道所有的对象都是由其对应的类实例化而来,在Objective-C中,我们用到的几乎所有类都是NSObject类的子类,NSObject类定义格式如下(忽略其方法声明)

@interface NSObject  {
  Class isa;
}

其中Class是typedef struct objc_class *Class;
objc_class指的又是:

struct objc_class {
  Class isa;
}

为什么连续出现2个Class isa?
对于实例对象,isa指向它所属的类,这很好理解。但是类的isa指向什么呢?要知道 类也是一个对象 ,而类所属的类别是 元类 ,类的isa指针就是指向所属的元类

实例对象-类对象-元类之间关系百度如下:

1.类对象的实质

类对象是由编译器创建的,即在编译时所谓的类,就是指类对象(官方文档中是这样说的: The class object is the compiled version of the class)。

任何直接或间接继承了NSObject的类,它的实例对象(instance objec)中都有一个isa指针,指向它的类对象(class object)。这个类对象(class object)中存储了关于这个实例对象(instace object)所属的类的定义的一切:包括变量,方法,遵守的协议等等。

因此,类对象能访问所有关于这个类的信息,利用这些信息可以产生一个新的实例,但是类对象不能访问任何实例对象的内容。当你调用一个 “类方法” 例如 [NSObject alloc],你事实上是发送了一个消息给他的类对象。

2.类对象和实例对象的区别

尽管类对象保留了一个类实例的原型,但它并不是实例本身。它没有自己的实例变量,也不能执行那些类的实例的方法(只有实例对象才可以执行实例方法)。然而,类的定义能包含那些特意为类对象准备的方法–类方法( 而不是的实例方法)。类对象从父类那里继承类方法,就像实例从父类那里继承实例方法一样。

类对象是一个功能完整的对象,所以也能被动态识别(dynamically typed),接收消息,从其他类继承方法。特殊之处在于它们是由编译器创建的,缺少它们自己的数据结构(实例变量),只是在运行时产生实例的代理。

元类

实际上,类对象是元类对象的一个实例!!

元类描述了 一个类对象,就像类对象描述了普通对象一样。不同的是元类的方法列表是类方法的集合,由类对象的选择器来响应。当向一个类发送消息时,objc_msgSend会通过类对象的isa指针定位到元类,并检查元类的方法列表(包括父类)来决定调用哪个方法。元类代替了类对象描述了类方法,就像类对象代替了实例对象描述了实例化方法。

很显然,元类也是对象,也应该是其他类的实例,实际上元类是根元类(root class’s metaclass)的实例,而根元类是其自身的实例,即根元类的isa指针指向自身。

类的super_class指向其父类,而元类的super_class则指向父类的元类。元类的super class链与类的super class链平行,所以类方法的继承与实例方法的继承也是并行的。而根元类(root class’s metaclass)的super_class指向根类(root class),这样,整个指针链就链接起来了!!

记住,当一个消息发送给任何一个对象, 方法的检查 从对象的 isa 指针开始,然后是父类。实例方法在类中定义, 类方法在元类和根类中定义。(根类的元类就是根类自己)。

总得来说:类对象存储实例对象的信息包括变量、实例方法,元类对象存储类对象的信息如类方法、版本号、名字

Paste_Image.png

object_getClass(obj)与[obj class]的区别:
1、object_getClass(obj)返回obj的isa指针
2、[obj class]
-若obj为实例对象,调用实例方法- (Class)class,则返回obj对象中的isa指针
-若obj为类对象,调用类方法+ (Class)class,返回其本身
3、-(Class)class的实现如下:

- (Class)class {
   return object_getClass(self); 
}

回到代码中,对于[self class]会被编译为

id objc_msgSend ( id self, @selector(class), ... )

消息接受者是self,自己并没有实现class的方法,然后去父类Father中寻找,也没有实现,一直到NSObjcet中找到,返回self的isa指针,self是Son的实例对象,isa指向Son,所以打印结果是Son。

[super class]
当使用到[super class]的时候会使用到objc_msgSendSuper函数,定义如下

id objc_msgSendSuper(struct objc_super *super, @selector(class), ...)

第一个参数是个objc_super的结构体,第二个参数还是类似上面的类方法的selector,
objc_super定义:

struct objc_super { 
  id receiver; 
  Class superClass;
};

那么调用[super class]后的内部流程如下:
1.当使用 [super class] 时,这时要转换成 objc_msgSendSuper 的方法。
2.先构造 objc_super 的结构体,第一个成员变量就是 self,第二个成员变量是 father,然后要找 class 这个 selector,先去 superClass 也就是father中去找,没有,然后去father的父类中去找,结果还是在 NSObject 中找到了。
3.然后内部使用函数 objc_msgSend(objc_super->receiver, @selector(class)) 去调用,此时已经和我们用 [self class] 调用时相同了,因为此时的 receiver 还是 Son的实例对象self,所以这里返回的也是 Son。

总结:super并不是指向父类的指针,它用来告诉编译器去父类的方法列表中查到调用的方法。所以用self调用方法的时候是先从本类开始找,找不到在到父类中找;如果用super调用,则从父类开始找,找不到就从父类的父类中开始找。

参考资料:http://gold.xitu.io/post/57a9516e7db2a2005aba4809

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

推荐阅读更多精彩内容

  • 转至元数据结尾创建: 董潇伟,最新修改于: 十二月 23, 2016 转至元数据起始第一章:isa和Class一....
    40c0490e5268阅读 1,715评论 0 9
  • 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的转载 这篇文章完全是基于南峰子老师博客的...
    西木阅读 30,557评论 33 466
  • 每当对着word想敲一篇发人深省的鸡汤文时,总觉得自己是机械地在脑中翻找切入点,搜索事例,打出一行字又迅速地删除,...
    阿渔渔阅读 340评论 4 5
  • 决定不考研了,就提前向老师申请开始做毕业设计了。本来老师给安排的课题是“Linux内存泄漏漏洞研究”,但我跟老师说...
    ACoder_bjs阅读 556评论 0 0