如何从Swift中调用一个Objective-C的可变参数方法?

41

假设我有一个Objective-c中的类,其中有一个静态方法如下:

+ (NSError *)executeUpdateQuery:(NSString *)query, ...;

我该如何在Swift中调用它?自动完成不能识别它,编译器也不满意:

MyClassName.executeUpdateQuery("")

抱怨'MyClassName.Type没有名为executeUpdateQuery的成员'

3个回答

51

编写一个 va_list 版本的可变参数方法;

+ (NSError *)executeUpdateQuery:(NSString *)query, ...
{
    va_list argp;
    va_start(argp, query);
    NSError *error = [MyClassName executeUpdateQuery: query args:argp];
    va_end(argp);
    
    return error;
}

+ (NSError *)executeUpdateQuery:(NSString *)query args:(va_list)args
{
    NSLogv(query,args);
    return nil;
}

这可以从Swift中调用

MyClassName.executeUpdateQuery("query %d, %d %d", args: getVaList([1,2,3,4]))

添加一个扩展以支持本地的Swift可变参数:

protocol CFormatFunction {
    class func executeUpdateQuery(_ format: String, _ args: CVarArg...) -> NSError?
}

extension MyClassName : CFormatFunction {
    class func executeUpdateQuery(_ format: String, _ args: CVarArg...) -> NSError?
    {
        return withVaList(args) { MyClassName.executeUpdateQuery(format, args: $0) }
    }
}

MyClassName.executeUpdateQuery("query %d %@ %.2f", 99, "Hello", 3.145)

注意,Swift 不提供 NS_FORMAT_FUNCTION 警告 (-Wformat)

MyClassName.executeUpdateQuery("query %@", 99)

我正在尝试使用您提供的扩展代码,但是出现了“使用未声明的类型'CVarArg'”错误。我该如何解决这个问题? - YogevSitton
5
将CVarArg替换为CVarArgType解决了问题。此外,为什么我需要这个协议? - YogevSitton
1
文档建议使用withVaList,并有些不祥地表示需要“尽量避免”使用getVaList。(http://swiftdoc.org/v2.1/func/getVaList/#func-getvalist_-cvarargtype) - divergio

8

CVArgType 在 Swift 中本地展示 C “varargs” API 中非常有用。(Swift 文档)

如果您有

+ (int)f1:(int)n, ...;

首先需要创建一个va_list版本:

+ (int)f2:(int)n withArguments:(va_list)arguments

通过从可变参数版本调用 va_list 版本,可以 避免重复代码。如果您没有编写原始的可变参数函数,则可能无法实现此操作(在此参考资料中有解释)。

一旦您拥有了这个方法,您就可以编写这个 Swift 包装器:

func swiftF1(x: Int, _ arguments: CVarArgType...) -> Int {
     return withVaList(arguments) { YourClassName.f2(x, withArguments :$0) }
}

请注意省略的外部参数名(在arguments之前有_),这使得swiftF1的调用语法就像普通的C变参函数一样:
swiftF1(2, some, "other", arguments)

注意,这个例子没有使用`getVaList`,因为文档中说最好避免使用它。
如果需要,你还可以将这个函数放在原始类的Swift扩展中。

1

在Objective C中

MyClassName.h

+ (BOOL)executeSQL:(NSString *)sql args:(va_list)args;

MyClassName.m

+ (BOOL)executeSQL:(NSString *)sql args:(va_list)arguments
{
    NSLogv(sql, arguments);

    sql = [[NSString alloc] initWithFormat:sql arguments:arguments];
    va_end(arguments);
}

Swift - 在其类中添加
完美工作

protocol CFormatFunction {
   class func executeSQLArg(format: String, _ args: CVarArgType...) -> Bool
}

extension MyClassName : CFormatFunction {
   class func executeSQLArg(format: String, _ args: CVarArgType...) -> Bool
   {
        return MyClassName(format, args:getVaList(args))
   }
 }

如何使用
Swift
MyClassName.executeSQLArg(query, "one","two",3)

目标C
[MyClassName executeSQLArg:query, @"one",@"two",@3]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接