super

319 阅读3分钟

之前对super的理解就是简单一股脑当做self来看就行,自己也没有更深刻的认识,也不知道为什么可以简单当做self来看,这里自己测试整理一下

测试用例如下

@interface Parent : NSObject
@end
@interface son : Parent
- (void)say;
@end
@implementation Parent
@end
@implementation son
- (void)say {
    NSLog(@“------------------------------");
    NSLog(@"[self class]--%@",[self class]);
    NSLog(@"[super class]--%@",[super class]);
    NSLog(@"[super superclass]--%@",[super superclass]);
    NSLog(@"[self superclass]--%@",[self superclass]);
    NSLog(@“------------------------------");
}

先看输出结果

[self class]--son
[super class]--son
[super superclass]--Parent
[self superclass]—Parent

可以看到,确实将super直接当做self来理解就行,那为啥可以这么简单粗暴呢

这个得从消息发送和消息查找说起

上面四个消息调用转成底层一点的代码简单表示如下

 objc_msg_send(self,@selector(say))
 objc_msg_sendSuper(self,@selector(say))
 objc_msg_sendSuper(self,@selector(say))
 objc_msg_send(self,@selector(say))

再看objc_msg_send()objc_msg_sendSuper()

在objc4-723版本中全局搜索objc_msgSendSuper,可以看到message.h文件中有如下定义

OBJC_EXPORT id _Nullable
objc_msgSend(id _Nullable self, SEL _Nonnull op, ...)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

OBJC_EXPORT id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)
    OBJC_AVAILABLE(10.0, 2.0, 9.0, 1.0, 2.0);

objc_msg_send这个好理解,俩参数,第一个是消息接受者,第二个是个SEL,而objc_msg_sendSuper的第一个参数是一个objc_super的结构体,第二个也是个SEL,进一步查看objc_super

/// Specifies the superclass of an instance. 
struct objc_super {
    /// Specifies an instance of a class.
    __unsafe_unretained _Nonnull id receiver;
    /// Specifies the particular superclass of the instance to message. 
#if !defined(__cplusplus)  &&  !__OBJC2__
    /* For compatibility with old objc-runtime.h header */
    __unsafe_unretained _Nonnull Class class;
#else
    __unsafe_unretained _Nonnull Class super_class;
#endif
    /* super_class is the first class to search */
};
#endif

通过源码可以发现,objc_super结构体内有俩成员,一个就是消息接受者,在上述两个调用对象为super的情况下,其实内部真正的消息接收者还是self,这就能解释为啥super可以简单理解为self的原因,看注释可以知道,该结构体作为第一个参数,其实是指定该类(即消息接受者)的父类,也就是说在查找方法的时候的查找位置,如果是self,那就直接从该类的缓存/方法列表查找,如果是super,那就直接从该类的父类的缓存/方法列表查找

按我个人浅薄理解,上述两个api看成如下情况即可

objc_msgSend(self,self,SEL

objc_msgSendSuper(self,superclass,SEL)

第一个参数是消息接收者,第二个参数是方法查找起始位置(类),第三个参数为SEL,至于苹果的做法,会是仅仅为了增加判断而已吗?看源码是这样,具体的原因,碍于自己水平太low,无法确定。。。

以上便是关于selfsuper的区别,再来分析一下实际发送消息classsuperclass

因为上述测试用例并没有实现classsuperclass方法,所以按照正常消息发送过程来查找的话,son类与parent类的缓存以及方法列表都是没结果的,最终会到NSObject当中去查找,而NSObject是有这两个方法的实现的,具体实现如下

- (Class *)class {
          return object_getClass(self);
    }
- (Class)superclass {
          return class_getSuperclass(object_getClass(self));
    }

同样查看源码实现

Class object_getClass(id obj){
    if (obj) return obj->getIsa();
    else return Nil;
}

Class class_getSuperclass(Class cls){
    if (!cls) return nil;
    return cls->superclass;
}

结论

用self和super调用方法区别在于

1.查找方法的起始位置不同

2.调用方法的真正接收者相同

基于以上结论,如果将测试用例中的son类的父类改成NSObject,结果应该是

son son NSObject NSObject

实际测试如下

[self class]--son
[super class]--son
[super superclass]--NSObject
[self superclass]—NSObject