【问题标题】:Swift doesn't convert Objective-C NSError** to throwsSwift 不会将 Objective-C NSError** 转换为 throws
【发布时间】:2016-04-19 13:46:34
【问题描述】:

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

- (void)doSomethingWithArgument:(ArgType)argument error:(NSError **)error

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

Swift 自动翻译产生错误的 Objective-C 方法 进入根据 Swift 的原生错误抛出错误的方法 处理功能。

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

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

此外,当我尝试使用它们时,它会引发运行时异常:

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

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

【问题讨论】:

  • 您使用的是什么版本的 Swift? 'throws' 仅在 Swift 2.0 及更高版本中可用。

标签: ios objective-c swift nserror


【解决方案1】:

只有Objective-C 方法被转换为抛出Swift 方法,它们确实返回BOOL(不是小写的bool)或可空对象。 (使用 Xcode 11.7 和 Swift 5 语言测试。)

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

重要提示:成功或失败由方法的返回值指示。 虽然在 Cocoa 错误中间接返回错误对象的 Cocoa 方法 如果方法指示失败,则域保证返回此类对象 通过直接返回 nil 或 NO,您应该始终检查返回 在尝试对 NSError 对象执行任何操作之前,值为 nil 或 NO。

比如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
}

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

否则你会从 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

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

【讨论】:

  • 谢谢,非常好的回答
  • 不错的发现!我在这里找到了属性的文档:github.com/apple/swift-clang/blob/…
  • 看起来这也适用于初始化程序:- (instancetype _Nullable)init:(NSError**)error;。但是,要从 Swift 调用它,我必须传递一个空元组 () 作为参数。
  • Martin,有没有办法将闭包转换为属性或参数?我找不到一个可行的例子,但一直在尝试(无济于事):typedef BOOL(^Snafu)(NSString*, NSError**);,方法为-(void) doSomethingWithClosure: (Snafu) closure;,属性为@property (nonatomic, copy) Snafu _Nullable snafu;。谢谢。
  • 我认为这回答了我的问题(也是一个很好的测试):stackoverflow.com/questions/49409191/…
【解决方案2】:

你需要让你的方法返回一个BOOL,告诉运行时应该或不应该抛出一个错误。此外,您应该在错误参数中添加__autoreleasing,以确保 ARC 在您有机会使用它之前不会意外释放错误:

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

然后你可以像这样从 Swift 中调用它:

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

【讨论】:

  • __autoreleasing 实际上默认为NSError **error 参数。否则你是对的。
  • 感谢您的提示...我今天学到的另一件事 :) SO postClang documentation 确认您所说的 :)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-10-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多