无法在Swift类中调用抛出并从Obj-C返回非void的方法

时间:2016-04-17 04:45:18

标签: objective-c swift exception

我希望在Swift中有一个返回Bool的函数,但如果发生异常,它也可以throw

例如:

 func doSomething(value: Int) throws -> Bool {
    if (value > 0) {
        return true
    } else if (value == 0) {
        throw NSError(domain: "SwiftClass", code: 0, userInfo: nil)
    }
    return false
}

这很好,来自Swift,但是如果我尝试使用Objective-C中的这个函数,编译器就找不到该方法。我知道throws要求Objective-C函数签名更改为doSomething:x error:&error,如果我将返回类型更改为Void -

func doSomething(value: Int) throws -> Void {
    if (value == 0) {
         throw NSError(domain: "SwiftClass", code: 0, userInfo: nil)
    } else if (value < 0) {
        throw NSError(domain: "SwiftClass", code: -1, userInfo: nil)
    }
}

但这有不同的语义。在第一个例子中,如果出现问题,我只需要处理异常(或非nil NSError)。使用此代码,我必须捕获异常(或检查错误)并确定它是真正的问题还是只是有效的“假”情况。

是否真的不可能使用具有在Objective-C上下文中抛出的非void返回的Swift函数?

1 个答案:

答案 0 :(得分:5)

正如我的评论中所述,我不清楚为什么这不应该起作用。 documentation没有任何建议表明这不应该有效,但它也没有明确说明应该这样做。我阅读文档说它应该有用。

话虽如此,我们可以将此包装在一个可从Objective-C调用的方法中,方法是将返回类型更改为Void并使用inout参数结果:

func doSomething(value: Int) throws -> Bool {
    if (value > 0) {
        return true
    } else if (value < 0) {
        return false
    } else {
        throw NSError(domain: "SwiftClass", code: 0, userInfo: nil)
    }
}

func doSomething(value: Int, inout result: Bool) throws {
    do {
        result = try doSomething(value)
    } catch let error {
        // If need be, assign some default value to result.
        throw error
    }
}

或者,根据你的评论,如果我们返回一个可以桥接到Objective-C类的值(显然不包含Bool),编译器似乎会很高兴,所以我们可以包装它:

@objc func doSomething(value: Int) throws -> NSNumber {
    do {
        return try doSomething(value)
    } catch let error {
        throw error
    }
}

在玩这个时,很明显为什么只有当我们返回一个可以映射到Objective-C类的值时才会起作用。

编译器不允许您从标有@objcthrows的方法中返回可选项。为什么?因为虽然在Swift方面,我们使用try语义来调用方法,但Objective-C中的方法完全不同。 nil用于表示失败。

因此尝试在Swift中使用签名创建一个方法:

@objc func doSomething(value: Int) throws -> NSNumber?

生成此警告:

  

投掷方法无法标记为@objc,因为它返回的值为可选类型&#39; NSNumber?&#39 ;; &#39;零&#39;表示没有达到Objective-C

最后,我仍然非常建议您将Swift方法编写为返回Bool的签名并编写一个返回NSNumber的包装器方法,以便我们仍然使用我们的Swift方法最准确的类型。

此外,如果您注意到,Bool 可以自动地自动加入NSNumber。我的包装器方法被写为返回类型NSNumber,但我包装的方法返回类型Bool,但Swift非常乐意从应该返回的方法返回Bool返回NSNumber。最有可能的麻烦是,默认情况下,如果未指定,Swift的Bool会转换为Objective-C的BOOL,这是一个非类。

如果您的返回类型为BOOL,则Objective-C无法区分三种可能的情况。它只有两个可能的BOOL方法返回:YESNO。将Bool映射到NSNumber后,它可以返回@YES@NOnil

如果是我,NSNumber对我来说不够严格。我可能会被鼓励为此编写自己的包装类。

@objc class BooleanObject: NSObject, BooleanType {
    let boolValue: Bool

    init(_ boolValue: Bool) {
        self.boolValue = boolValue
    }
}

请注意,BooleanType协议意味着Swift仍然非常乐意使用此类型的变量,就像它们是常规Bool一样。

let b = BooleanObject(true)

if b {
    // b's boolValue property is true
}