• [super performSelector:sel]探秘


    Super

    @interface Super : NSObject
    
    @end
    
    @implementation Super
    - (void)log{
        NSLog(@"super");
    }
    @end
    

    Child

    @interface Child : Super
    
    @end
    
    @implementation Child
    - (void)log{
    	if([super respondsToSelector:@selector(log)]){
    	    [super performSelector:@selector(log)];
    	}
    }
    @end
    

    Child中的log调用能成功吗?
    这种情况可能很多朋友都遇到过,在重写父类的过程中,想调用一个父类没有公开的方法,可能最省事的办法就是使用performSelector了(当然这是不好的设计方式),直觉上觉得这样当然没问题啦,可是一运行却发现代码会陷入死循环,就拿上面的例子来说会发现一直在递归调用Child里的log方法,why???

    要搞清楚这个问题,还需要从runtime入手,首先,让我们来看看super是怎么回事?

    让我们重写下我们的objc代码,可以看到如下的代码片段:

     ((id (*)(__rw_objc_super *, SEL, SEL))(void *)objc_msgSendSuper)((__rw_objc_super){(id)self, (id)class_getSuperclass(objc_getClass("Child"))}, sel_registerName("performSelector:"), sel_registerName("log"));
    

    是的,super最终会变成对objc_msgSendSuper方法的调用,而关于objc_msgSendSuper的文档是这样写的:

    /** 
     * Sends a message with a simple return value to the superclass of an instance of a class.
     * 
     * @param super A pointer to an c objc_super data structure. Pass values identifying the
     *  context the message was sent to, including the instance of the class that is to receive the
     *  message and the superclass at which to start searching for the method implementation.
     * @param op A pointer of type SEL. Pass the selector of the method that will handle the message.
     * @param ...
     *   A variable argument list containing the arguments to the method.
     * 
     * @return The return value of the method identified by e op.
     * 
     * @see objc_msgSend
     */
    

    而它的第一个参数定义大致如下:

     struct objc_super {
        /// Specifies an instance of a class.
        __unsafe_unretained id receiver;
        /// Specifies the particular superclass of the instance to message. 
        __unsafe_unretained Class super_class;
        /* super_class is the first class to search */
    };
    

    所以,super的作用其实可以理解为两个:

    • 告诉runtime从父类开始找SEL的实现

    • 再说第二个作用之前,需要看看一个实例方法对应的IMP是什么样子的,还是看看之前重写后的Child上的log的IMP:

        static void _I_Child_log(Child * self, SEL _cmd) {...}
      

    可见,在调用最终的c函数时,runtime会把当前实例作为第一个参数传递进去(这与很多其他面向对象的语言行为类似)。而super的第二个作用则正是要告诉runtime在执行第一步找到的IMP时self应该传什么,结合前面的内容,可以看到其实传递的就是当前的实例(这样才能保证多态)。

    然后我们再来看看performSelector的实现:

    - (id)performSelector:(SEL)sel {
        if (!sel) [self doesNotRecognizeSelector:sel];
        return ((id(*)(id, SEL))objc_msgSend)(self, sel);
    }
    

    绕了一圈,[super performSelector:sel]其实就是在执行objc_msgSend(self, sel), 因此对于之前出现的现象也就不会觉得奇怪了。

  • 相关阅读:
    Linux crontab定时执行任务 命令格式与详细例子
    git的color configura
    Linux不用使用软件把纯文本文档转换成PDF文件的方法
    linux sar命令详细说明相关参数
    linux下sar tool command note
    Linux see 网卡当前流量
    安装调试Installing Odoo
    寻找[nginx] 由Lua 粘合的Nginx生态环境-- agentzh
    git检查与放弃本地的代码修改情况
    发现linux shell中$0,$?,$!等的特殊用法
  • 原文地址:https://www.cnblogs.com/MythYsJh/p/super-perform-selector.html
Copyright © 2020-2023  润新知