您的位置:首页 > 移动开发 > Objective-C

刨根问底Objective-C Runtime(1)- Self & Super

2015-07-22 18:50 746 查看



刨根问底Objective-C Runtime(1)- Self & Super

前言

关于Objective-C Runtime一篇好的文档 : Understanding the Objective-C Runtime

译文地址为: http://blog.cocoabit.com/blog/2014/10/06/yi-li-jieobjective-cruntime/

Objective-C Runtime源码是开源的,下载地址为: http://opensource.apple.com/tarballs/objc4/

习题内容

@唐巧_boy在微博上分享了他们技术讨论会关于objc runtime的讨论习题内容,习题来自 sunnyxx, 他的博客地址为http://blog.sunnyxx.com

以下是习题内容(图片转自@唐巧_boy微博):



自己做完这些题之后,也顺便复习了一些Objective-C Runtime的知识,现在整理一下,分享给大家。

该笔记分为四篇:

刨根问底Objective-C Runtime(1)- Self & Super
刨根问底Objective-C Runtime(2)- Object & Class & Meta Class
刨根问底Objective-C Runtime(3)- 消息和Category
刨根问底Objective-C Runtime(4)- 成员变量与属性

刨根问底

下面的代码输出什么?

[code]@implementation Son : Father
- (id)init
{
    self = [super init];
    if (self)
    {
        NSLog(@"%@", NSStringFromClass([self class]));
        NSLog(@"%@", NSStringFromClass([super class]));
    }
    return self;
}
@end

答案:都输出 Son

[code]2014-11-05 11:06:18.060 Test[8566:568584] NSStringFromClass([self class]) = Son
2014-11-05 11:06:18.061 Test[8566:568584] NSStringFromClass([super class]) = Son

解惑:这个题目主要是考察关于objc中对 self 和 super 的理解。

self 是类的隐藏参数,指向当前调用方法的这个类的实例。而 super 是一个 Magic Keyword, 它本质是一个编译器标示符,和 self 是指向的同一个消息接受者。上面的例子不管调用[self class]还是[super class],接受消息的对象都是当前 Son *xxx 这个对象。而不同的是,super是告诉编译器,调用 class 这个方法时,要去父类的方法,而不是本类里的。

当使用 self 调用方法时,会从当前类的方法列表中开始找,如果没有,就从父类中再找;而当使用 super 时,则从父类的方法列表中开始找。然后调用父类的这个方法。

真的是这样吗?继续看:

使用clang重写命令:

[code]$ clang -rewrite-objc test.m

发现上述代码被转化为:

[code]    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gm_0jk35cwn1d3326x0061qym280000gn_T_main_a5cecc_mi_0, NSStringFromClass(((Class (*)(id, SEL))(void *)objc_msgSend)((id)self, sel_registerName("class"))));

    NSLog((NSString *)&__NSConstantStringImpl__var_folders_gm_0jk35cwn1d3326x0061qym280000gn_T_main_a5cecc_mi_1, NSStringFromClass(((Class (*)(__rw_objc_super *, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){ (id)self, (id)class_getSuperclass(objc_getClass("Son")) }, sel_registerName("class"))));

从上面的代码中,我们可以发现在调用
[self
 class]
时,会转化成
objc_msgSend
函数。看下函数定义:

[code]id objc_msgSend(id self, SEL op, ...)

我们把
self
做为第一个参数传递进去。

而在调用
[super
 class]
时,会转化成
objc_msgSendSuper
函数。看下函数定义:

[code]id objc_msgSendSuper(struct objc_super *super, SEL op, ...)

第一个参数是
objc_super
这样一个结构体,其定义如下:

[code]struct objc_super {
   __unsafe_unretained id receiver;
   __unsafe_unretained Class super_class;
};

结构体有两个成员,第一个成员是 receiver, 类似于上面的 objc_msgSend函数第一个参数self 。第二个成员是记录当前类的父类是什么。

所以,当调用
[self
 class]
时,实际先调用的是
objc_msgSend
函数,第一个参数是
Son
当前的这个实例,然后在
Son
这个类里面去找
-
 (Class)class
这个方法,没有,去父类
Father
里找,也没有,最后在
NSObject
类中发现这个方法。而
-
 (Class)class
的实现就是返回self的类别,故上述输出结果为 Son。

objc Runtime开源代码对
-
 (Class)class
方法的实现:

[code]- (Class)class {
    return object_getClass(self);
}

而当调用
[super
 class]
时,会转换成
objc_msgSendSuper
函数。第一步先构造 objc_super 结构体,结构体第一个成员就是 self 。第二个成员是 (id)class_getSuperclass(objc_getClass(“Son”)) ,
实际该函数输出结果为 Father。第二步是去 Father这个类里去找
-
 (Class)class
,没有,然后去
NSObject
类去找,找到了。最后内部是使用
objc_msgSend(objc_super->receiver,
 @selector(class))
去调用,此时已经和
[self
 class]
调用相同了,故上述输出结果仍然返回 Son。

下一篇博客的主要分享的内容是关于Objective-C
 Runtime中的Object & Class & Meta Class学习笔记。


本文是关于Objective C Runtime的学习笔记。有不对的地方,欢迎大家指正。
感谢@唐巧_boy@sunnyxx分享题目。
本文由@Chun发表于Chun
Tips .
版权声明:自由转载-非商用-非衍生-保持署名 | Creative Commons BY-NC-ND 3.0

分享到:9

Posted by Chun Ye Nov 5th, 2014 5:11
am objective-c runtime
刨根问底Objective-C
Runtime(2)- Object & Class & Meta Class »

Comments

最新最早最热

15条评论
2条新浪微博





流水花落去
学习了,写得真好!
2014年11月7日回复顶转发



Mario
又找到了一个好博客了
2014年11月7日回复顶转发





汪士奇
讲的很清晰。
2014年11月12日回复顶转发





淘子回
感谢分享。三个问题(1)在继承链上查找Selector的时候,无论什么时候*self*都是指向当前实例对象的吗?(2)你之前说“super是告诉编译器,调用 class 这个方法时,要去父类的方法,而不是本类里的”,那为什么后面出来的结果完全不同?(3)objc_msgSend(objc_super->receiver, @selector(class)),这个调用是发生在哪里的,?有没有源代码的链接可以供参考?
2014年11月16日回复顶转发





淘子回
另外,“第二步是去 Father这个类里去找- (Class)class”,每个类不是都有class成员方法的吗?为什么会找不到。谢谢。
2014年11月16日回复顶转发





Chun Ye
回复 淘子回: Hi~

~

问题1: self是指向当前类的实例对象

问题2: 哪边结果不同? Son的实例调用 [super class]时, 在Father中找class方法 没找到,然后会在NSObject中去找,NSObject实现了该方法,但是class方法的实现是 return object_getClass(self)

问题3: 这个是对clang rewrite代码后的翻译
2014年11月16日回复顶转发





Chun Ye
回复 淘子回: Father类中并没有 - (Class)class 方法, 这个方法是在NSObject中实现的,可以读下接下来的几篇关于runtime的博客,有更加详细的分析。http://chun.tips/blog/2014/11/06/bao-gen-wen-di-objective%5Bnil%5Dc-runtime(3)%5Bnil%5D-xiao-xi-he-category/
2014年11月16日回复顶转发





淘子回
理解了。你的“找”的意思是“找当前类自身定义的方法”,我理解错了。顺便补充一下,我还查到,[self superclass]可以获得父类的Class对象。感谢你的分享,很精彩。:D
2014年11月16日回复顶转发





Chun Ye
回复 淘子回: 是的,

这个是superclass方法的实现 - (Class)superclass
{

return [self class]->superclass;

}
2014年11月16日回复顶转发





Pavel1986
objc_msgSend(objc_super->receiver, @selector(class))去调用,此时已经和[self class]调用相同了

这句话可能有点歧义,比如我在son中实现了class,

- (Class)class

{

return nil;

}

调用NSLog之后,会输出 null和son。如果二者已经调用相同了,则应该都输出null才对。所以我谨慎怀疑objc_msgSend(objc_super->receiver, @selector(class))这句话,应该还有个标识,告知运行的时候直接去父类找。不知道博主怎么看。
2014年12月4日回复顶转发





Chun Ye
回复 Pavel1986: Hi, 你好~ 文中所说的相同,是指在那样的case下,Som并没有重写 -(Class)class方法的实现。而在你这个case中,son重写了case的实现,返回nil。所以当调用 [self class]时,会返回null, 而当调用 [super class]时,先构造super的结构体,super结构体有一个成员是self。构造完结构体之后,会直接在
super对应的类中寻找class方法的实现,这个时候并没有受到你再Son中重写方法的影响。而是正常调用:

- (Class)class {

return object_getClass(self);

}

而 self 是 Son的实例, 所以返回 Son。

附 object_getClass 方法的实现如下:

Class object_getClass(id obj)

{

if (obj) return obj->getIsa();

else return Nil;

}
2014年12月5日回复顶转发





shuiyouren
对oc运行时的理解的确帮助很大!
2014年12月18日回复顶转发





帅到治国
谢谢
1月19日回复顶转发



Ryan
最后内部是使用 objc_msgSend(objc_super->receiver, @selector(class))去调用,此时已经和[self class]调用相同了,故上述输出结果仍然返回 Son。

这句话中为什么一定是调用objc_super-receiver 而不是调用 objc_super->super_class
4月24日回复顶转发



Ryan
刚刚又研究了一下 class_getSuperclass 是去获得father 类,但是调用class 方法是是调用的 object_getClass(self) 这个self 反推 objc_super->receriver

不知道是这个意思吗?
4月24日回复顶转发

社交帐号登录:

微博
QQ
人人
豆瓣
更多»





发布

Chun Tips正在使用多说

Copyright © 2015 - Chun Ye - Powered by Octopress
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: