函数调用的参数太多,预期为 0,有 2

Too many arguments to function call, expected 0, have 2

去年我们从 Pixate 转移到 StylingKit

https://github.com/StylingKit/StylingKit 据我所知仍在开发中的 Pixate 分支。

然而,随着 XCode 11 的发布,我们现在遇到了问题,在以前的版本中它工作得很好,但现在无法编译。

    void* callSuper0(id self, Class superClass, SEL _cmd)
    {
        struct objc_super super;
        super.receiver = (__bridge void *)self;
        super.class = superClass != NULL ? superClass : class_getSuperclass(object_getClass(self));
        return objc_msgSendSuper(&super, preprocessSEL(_cmd));
    }

return objc_msgSendSuper(&super, preprocessSEL(_cmd));

抛出错误“函数调用的参数太多,预期为 0,有 2”

我尝试更改一些建筑设置,如 Too many arguments to function call, expected 0, have 3 中所示,特别是“启用对 objc_msgSend 调用 NO 的严格检查”,但没有帮助。

如有任何帮助,我们将不胜感激。

注意:下面的代码只是一个片段,没有被执行,是由Xcode10&11编译的,可能有错误或者其他问题。

在 Xcode 中,右键单击 objc_superobjc_msgSendSuper 以及 select "Jump to Definition",这将打开包含定义的系统文件。这将显示两件事:

  1. objc_super 的字段名称和类型自您的代码编写以来已发生变化,其中一些已记录在注释中。
  2. 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

附录

根据您的评论和代码 post 作为另一个答案。

在你的代码中你写:

void* (*objc_msgSendSuperTyped)(id self, SEL _cmd) = (void*)objc_msgSendSuper;
return objc_msgSendSuperTyped((id) &super, preprocessSEL(_cmd));

制作函数指针的类型化副本,而不是像我们之前的代码那样仅在 use 上强制转换,这很好。但是,当您尝试将非保留类型(&super - 结构指针)转换为保留类型(id)时,您的代码应该从 Xcode 产生错误 - 这需要桥铸

这里的解决方法是给objc_msgSendSuperTyped正确的类型,它的第一个参数应该是一个结构指针:

void* (*objc_msgSendSuperTyped)(struct objc_super *self, SEL _cmd) = (void*)objc_msgSendSuper;
return objc_msgSendSuperTyped(&super, preprocessSEL(_cmd));

来自您的评论:

I had to change super.super_class to super.class for it to work, it now throws other errors unrelated.

这表明您有一些不寻常的设置,在 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__ 显然没有为您尝试转换的源定义...

我发现它可以通过更改为:

void* callSuper0(id self, Class superClass, SEL _cmd)
{
    struct objc_super super;
    super.receiver = (__bridge void *)self;
    super.class = superClass != NULL ? superClass : class_getSuperclass(object_getClass(self));

    void* (*objc_msgSendSuperTyped)(id self, SEL _cmd) = (void*)objc_msgSendSuper;
    return objc_msgSendSuperTyped((id) &super, preprocessSEL(_cmd));
}