[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)
, 因此对于之前出现的现象也就不会觉得奇怪了。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 分享 3 个 .NET 开源的文件压缩处理库,助力快速实现文件压缩解压功能!
· Ollama——大语言模型本地部署的极速利器
· [AI/GPT/综述] AI Agent的设计模式综述