简体   繁体   English

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

[英]Unable to call method in Swift class that throws and has non-void return from Obj-C

I want to have a function in Swift that returns a Bool but that can also throw if an exception occurs. 我希望在Swift中有一个返回Bool的函数但是如果发生异常也可以throw

For example: 例如:

 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
}

This works fine, from Swift, but if I try and use this function from Objective-C, the compiler can't find the method. 这很好,来自Swift,但如果我尝试使用Objective-C中的这个函数,编译器就找不到该方法。 I know that the throws requires the Objective-C function signature to change to doSomething:x error:&error and this works, if I change the return type to Void - 我知道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)
    }
}

But this has different semantics. 但这有不同的语义。 In the first example, I only need to deal with the exception (or non-nil NSError ) if there is a problem. 在第一个例子中,如果出现问题,我只需要处理异常(或非nil NSError )。 With this code I have to catch the exception (or examine the error) and determine if it is a real problem or just the valid, "false" case. 使用此代码,我必须捕获异常(或检查错误)并确定它是真正的问题还是只是有效的“假”情况。

Is it really not possible to use a Swift function with a non-void return that throws in an Objective-C context? 是否真的不可能使用具有在Objective-C上下文中抛出的非void返回的Swift函数?

As noted in my comments, I'm unclear as to why this shouldn't work. 正如我的评论中所指出的,我不清楚为什么这不起作用。 The documentation doesn't give anything to suggest this shouldn't work, but it also doesn't explicitly state that it should. 文档没有给出任何建议,这不应该工作,但它也没有明确说明它应该。 My reading of the documentation says that it should work. 我阅读文档说它应该有效。

With that said, we could wrap this in a method that is callable from Objective-C by changing the return type to Void and using an inout parameter for the result: 话虽如此,我们可以将这个包装在一个可以从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
    }
}

Alternatively, as per your comment, it seems the compiler would be happy if we returned a value that can bridge to an Objective-C class (which apparently doesn't include Bool ), so we could wrap it as such: 或者,根据你的评论,如果我们返回一个可以桥接到Objective-C类(显然不包含Bool )的值,编译器会很高兴,所以我们可以这样包装它:

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

And in playing around with this, it became clear why this only works when we're returning a value that can map to an Objective-C class. 在解决这个问题时,很明显为什么只有当我们返回一个可以映射到Objective-C类的值时,这才有效。

The compiler will not let you return an optional from a method marked with @objc and throws . 编译器不允许您从标有@objcthrows的方法返回一个可选项。 Why? 为什么? Because while on the Swift side, we use try semantics to call the method, the approach is entirely different in Objective-C. 因为在Swift方面,我们使用try语义来调用方法,Objective-C中的方法完全不同。 nil is used to indicate failure. nil用于表示失败。

So trying to create a method with this signature in Swift: 所以尝试在Swift中创建一个带有签名的方法:

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

Generates this warning: 生成此警告:

Throwing method cannot be marked @objc because it returns a value of optional type 'NSNumber?'; 抛出方法不能标记为@objc,因为它返回一个可选类型'NSNumber?'的值; 'nil' indicates failure to Objective-C 'nil'表示没有Objective-C


In the end though, I do still very much recommend that you write the Swift method as having the signature returning Bool and write a wrapper method returning NSNumber so that we still have our Swift method with the most accurate type possible. 最后,我仍然非常建议您将Swift方法编写为返回Bool的签名并编写一个返回NSNumber的包装器方法,以便我们仍然可以使用最准确类型的Swift方法。

Also, if you notice, Bool can happily automatically box up into NSNumber . 另外,如果你注意到, Bool 可以很自然地自动装入NSNumber My wrapper method was written as returning type NSNumber , but the method I'm wrapping returns type Bool , yet Swift was perfectly happy to return Bool from a method that is supposed to return NSNumber . 我的包装器方法被写为返回类型NSNumber ,但我正在包装的方法返回类型Bool ,但Swift非常乐意从一个应该返回NSNumber的方法返回Bool The trouble most likely is that by default, if unspecified, Swift's Bool translates into Objective-C's BOOL , which is a non-class. 最麻烦的是,默认情况下,如果未指定,Swift的Bool转换为Objective-C的BOOL ,这是一个非类。

Objective-C wouldn't be able to distinguish between the three possible cases if your return type is BOOL . 如果您的返回类型是BOOL Objective-C将无法区分三种可能的情况。 It only has two possible returns from a BOOL method: YES or NO . 它只有两个可能的BOOL方法返回: YESNO With Bool mapped to an NSNumber , it can return @YES , @NO , and nil . Bool映射到NSNumber ,它可以返回@YES@NOnil

And if it's me, NSNumber isn't really type strict enough for me. 如果是我, NSNumber对我来说不够严格。 I might be encouraged to write my own wrapped class for this. 我可能会被鼓励为此编写自己的包装类。

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

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

Note that the BooleanType protocol means that Swift would still be perfectly happy to use variables of this type as if they were a regular Bool . 请注意, BooleanType协议意味着Swift仍然非常乐意使用这种类型的变量,就好像它们是常规的Bool

let b = BooleanObject(true)

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM