[英]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
. 编译器不允许您从标有
@objc
并throws
的方法返回一个可选项。 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
方法返回: YES
或NO
。 With Bool
mapped to an NSNumber
, it can return @YES
, @NO
, and nil
. 将
Bool
映射到NSNumber
,它可以返回@YES
, @NO
和nil
。
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.