zoukankan      html  css  js  c++  java
  • Objective-C Runtime初探:self super

    题目

    上题目,已知A是爷爷,B是爸爸,C是孙子。

    @interface A : NSObject
    - (void)f;
    @end
    
    @interface B : A
    - (void)f;
    - (void)g;
    @end
    
    @interface C : B
    - (void)f;
    @end

    A,B,C都各自实现了函数f,只有B实现了函数g。

    @implementation A
    - (void)f
    {
        NSLog(@"A");
    }
    @end
    
    
    @implementation B
    - (void)f
    {
        NSLog(@"B");
    }
    
    - (void)g
    {
        [self f];
        [super f];
        NSLog(@"%@", [self class]);
        NSLog(@"%@", [super class]);
    }
    @end
    
    @implementation C
    - (void)f
    {
        NSLog(@"C");
    }
    @end

    请问,下面代码输出什么?

    C *c = [[C alloc] init];
    [c g];

    答案是CACC.

    分析

    1.C中没有g,为什么可以调用B的方法

    C继承自B,所以C中虽然没有g方法,但是会去从下往上找,找到B类的g方法进行调用。

    2.关于[self f]

    调用B的g的过程中,self其实是C,因为此时函数g消息的接受者是c。所以系统从self的类(即C)开始从下往上找,因为C中实现了f,所以直接输出C。

    3.关于[super f]

    这个问题是最困惑我的,卧槽,居然输出的是A而不是B?!

    首先要明确的一点是,self和super不是同一个层面上的东西。

    self是什么?

    self 是类的隐藏的参数,指向当前当前调用方法的类,另一个隐藏参数是 _cmd,代表当前类方法的 selector。

    super是什么?

    super可不是想象中的self.superclass啊

    super是Objective-C的语法糖,是一个编译器指示符,像宏一样,运行时XCode可不认识super这个东西。

    通过 clang 命令重写 Objective-C 代码

    clang -rewrite-objc test.m

    可以发现其实Objective-C的函数调用的本质

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

    省略号表示不定参数。

    即[self f]的本质是 objc_msgSend(self, @selector(f), "");

    所以,这点跟C++不同,并不是self去调用了函数f,而是消息f发送给了self,self是消息接受者。正是因为这样,Objective-C中[nil f]这种写法没问题,因为发送给nil的任何消息都返回nil。

    objc_msgSend(theReceiver, op, ...)的本质是,从theReceiver所在的类开始找,不断往上找,看看哪个类实现了op,一旦找到则将消息发送给它处理。

    但是[super f] 可不就是这样了,[super f]本质

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

    省略号表示不定参数。

    即[super f]的本质是 objc_msgSendSuper(super, @selector(f));

    objc_super何许人也?

    struct objc_super {
       __unsafe_unretained id receiver;
       __unsafe_unretained Class super_class;
    };

    objc_super的receiver是什么?

    reveiver是当前消息的接受者,代码里写的是[c g],所以super的receiver就是C.你也可以理解为就是self。

    objc_super的super_class是什么?

    super_class是当前super所在代码的类的父类,很绕口,但是这句一定要好好理解。比如无论谁调用了g,是C也好是B也罢,g中[super f]的super的super_class一定是A,因为super这5个字母是写在B里面的。

    objc_msgSendSuper(super, op, ...)的本质是,从objc_super->super_class所在的类开始找,不断往上找,看看哪个类实现了op,一旦找到了则将消息发送给它处理。

    在编译的时候,编译器看到了super,会因此构造一个struct。objc_super->receiver填了C,objc_super->super_class填了A。因此运行时编译器变不认识了super。

    如果你已经清楚了上面的逻辑,请回头看看[super f]。

    [super f],super->reveiver是C,super->super_class是A。所以从A开始找f,找到了则系统可能这样调用

    objc_msgSend(super->recevier, @selector(f))  //此时f是已经找到了的f,即A的f

    4.关于[self class]

    首先非常有必要看看class的实现,参考自苹果RunTime开源代码

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

    可以看到class返回的是self的类名。

    好,继续,[self class]中,class也是一个消息,接受者是self,也就是C,所以系统从C开始找class函数的实现,没找到,找B,找A都找不到,到了NSObjcect这一层找到了。

    此时调用了NSObject的class方法,那此时self是C,那就返回了C。

    5.关于[super class]

    这个问题一开始看起来也很奇怪,居然[self class]和[super class]输出了同一个东西,这不科学啊。但是把上面的知识理解好了,也就不奇怪了。

    执行[super class]时,编译器将[super class]替换为

    objc_msgSendSuper(super, @selector(class))

    其中super为reveiver=C,superclass=A。

    系统从A开始找class,找不到,到了NSObject这一层找到了 class 方法

    objc_msgSend(super->receiver, @selector(class))

    此时调用了NSObject的 class 方法,那此时self是C,那就返回了C

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

    不仅仅是 [super class],其他诸如 [super init] 的方法,都是同一个流程:

    1. 从 super->super_class 开始查找 IMP

    2. 执行 objc_msgSend(super->receiver, SEL)

    注意最终进行 objc_msgSend 的第一个参数始终是 super->receiver,而不是 super->super_class

    总结

    道理懂了之后就是思考的效率问题了,凡是self的就从消息接受者的类开始从下往上找;凡是super的就从写super的这个类开始往上找。

    参考链接

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

    Objective-C 中Self 和 Super 详解

    苹果RunTime开源代码

    Objective-C Super的理解

  • 相关阅读:
    iOS9TableView分割线默认不显示,只有滑动的时候才显示 解决办法
    bug调试大全
    清理缓存
    Objective-C文件和目录操作,IOS文件操作,NSFileManager使用文件操作
    dispatch_async 与 dispatch_get_global_queue 的使用方法
    ios UISearchController
    开发报错调试总结
    返回查询结果的id返回插入数据的id值
    前端监听事件
    datetime-local设置初始值
  • 原文地址:https://www.cnblogs.com/chenyg32/p/4811516.html
Copyright © 2011-2022 走看看