Swift 不会将 Objective-C NSError** 转换为抛出

Swift doesn't convert Objective-C NSError** to throws

我有一些 Objective-C 遗留代码,它声明了类似

的方法
- (void)doSomethingWithArgument:(ArgType)argument error:(NSError **)error

这里写的 https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html

Swift automatically translates Objective-C methods that produce errors into methods that throw an error according to Swift’s native error handling functionality.

但是在我的项目中描述的方法是这样调用的:

object.doSomething(argument: ArgType, error: NSErrorPointer)

此外,当我尝试像这样使用它们时它会抛出运行时异常:

let errorPtr = NSErrorPointer()
object.doSomething(argumentValue, error: errorPtr)

我需要更多的东西来将 Objective-C "NSError **" 方法转换为 Swift "trows" 方法吗?

您需要将方法 return 设为 BOOL,以告知运行时应该或不应该抛出错误。您还应该将 __autoreleasing 添加到错误参数,以确保 ARC 在您有机会使用它之前不会意外释放错误:

- (BOOL)doSomethingWithArgument:(ArgType)argument error:(NSError * __autoreleasing *)error

然后您可以从 Swift 调用它,如下所示:

do {
    object.doSomethingWithArgument(someArgument)
} catch let err as NSError {
    print("Error: \(err)")
}

只有Objective-C方法被翻译成抛出Swift方法,它做return一个BOOL(不是lower-casedbool),或 nullable-object。 (使用 Xcode 11.7 和 Swift 5 语言测试。)

原因是 Cocoa 方法总是使用 return 值 NOnil 来指示方法的失败,而 not 只是设置一个错误对象。 这记录在 Using and Creating Error Objects:

Important: Success or failure is indicated by the return value of the method. Although Cocoa methods that indirectly return error objects in the Cocoa error domain are guaranteed to return such objects if the method indicates failure by directly returning nil or NO, you should always check that the return value is nil or NO before attempting to do anything with the NSError object.

例如Objective-C界面

@interface OClass : NSObject

NS_ASSUME_NONNULL_BEGIN

-(void)doSomethingWithArgument1:(int) x error:(NSError **)error;
-(BOOL)doSomethingWithArgument2:(int) x error:(NSError **)error;
-(NSString *)doSomethingWithArgument3:(int) x error:(NSError **)error;
-(NSString * _Nullable)doSomethingWithArgument4:(int) x error:(NSError **)error;
-(BOOL)doSomething:(NSError **)error;

NS_ASSUME_NONNULL_END

@end

映射到 Swift 为

open class OClass : NSObject {

    open func doSomethingWithArgument1(x: Int32, error: NSErrorPointer)
    open func doSomethingWithArgument2(x: Int32) throws
    open func doSomethingWithArgument3(x: Int32, error: NSErrorPointer) -> String
    open func doSomethingWithArgument4(x: Int32) throws -> String
    open func doSomething() throws
}

如果您可以更改方法的接口,那么您应该添加一个布尔值 return 表示成功或失败的值。

否则你会把它从 Swift 称为

var error : NSError?
object.doSomethingWithArgument(argumentValue, error: &error)
if let theError = error {
    print(theError)
}

备注:

我发现 Clang 有一个属性强制函数在 Swift 中抛出错误:

-(void)doSomethingWithArgument5:(int) x error:(NSError **)error
  __attribute__((swift_error(nonnull_error)));

映射到 Swift 为

public func doSomethingWithArgument5(x: Int32) throws

并且似乎“按预期”工作。但是,我找不到任何官方文档 关于这个属性,所以依赖它可能不是一个好主意。