注意:下面的代码只是一个片段,并没有被执行,但是它是由 Xcode 10 & 11 编译的。可能有错误或其他问题。
在 Xcode 中右键单击 objc_super 和 objc_msgSendSuper 并选择“跳转到定义”,这将打开包含定义的系统文件。这将显示两件事:
- 自编写代码以来,
objc_super 的字段名称和类型都发生了变化,其中一些记录在 cmets 中。
-
objc_msgSendSuper 的原型也已更改为 void objc_msgSendSuper(void),之前的原型也在 id _Nullable
objc_msgSendSuper(struct objc_super * _Nonnull super, SEL _Nonnull op, ...)。使用之前的原型,可以在代码中调用此函数而无需强制转换,而使用这个新原型则需要强制转换。
对代码进行更改会给出:
// A typedef for objc_msgSendSuper which takes only the required (first two) arguments
typedef void *(*NoAdditionalArgMsgSendSuper)(struct objc_super *super_class, SEL selector);
void* callSuper0(id self, Class superClass, SEL _cmd)
{
struct objc_super super;
// .receiver is now defined as "__unsafe_unretained _Nonnull id" so no bridge cast required
super.receiver = self;
// .super_class is new name for .class
super.super_class = superClass != NULL ? superClass : class_getSuperclass(object_getClass(self));
// cast objc_msgSendSuper to the correct type and then call
return ((NoAdditionalArgMsgSendSuper)objc_msgSendSuper)(&super, preprocessSEL(_cmd));
}
HTH
附录
按照您的评论和代码发布作为另一个答案。
在您编写的代码中:
void* (*objc_msgSendSuperTyped)(id self, SEL _cmd) = (void*)objc_msgSendSuper;
return objc_msgSendSuperTyped((id) &super, preprocessSEL(_cmd));
制作函数指针的类型副本,而不是像我们之前的代码一样在使用时进行强制转换,这很好。但是,当您尝试将非保留类型(&super - 结构指针)转换为保留类型(id)时,您的代码应该会从 Xcode 产生错误——这需要桥接。
这里的解决方案是给objc_msgSendSuperTyped正确的类型,它的第一个参数应该是一个结构体指针:
void* (*objc_msgSendSuperTyped)(struct objc_super *self, SEL _cmd) = (void*)objc_msgSendSuper;
return objc_msgSendSuperTyped(&super, preprocessSEL(_cmd));
来自您的评论:
我必须将 super.super_class 更改为 super.class 才能工作,现在它会抛出其他不相关的错误。
这表明你有一些不寻常的设置,在objc_super的声明中我们发现:
#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
__OBJC2__ 在 Xcode 11(以及许多早期版本)中默认为 .m 和 .mm 文件定义,__cplusplus 仅为 .mm 文件定义。所以条件!defined(__cplusplus) && !__OBJC2__ 是false,因此该字段被命名为super_class...
您可能希望确定为什么 __OBJC2__ 显然没有为您尝试转换的源定义...