【问题标题】:Using An Objective-C Function that Returns an Optional or Throw in Swift在 Swift 中使用返回 Optional 或 Throw 的 Objective-C 函数
【发布时间】:2016-03-15 05:17:47
【问题描述】:

我正在开发一个需要与现有 Objective-c api 交互的快速项目。不过,我在其中一个功能上遇到了一些障碍。在objective-c头文件(OrderItem.h)中我有这个函数定义:

+ (NSString *_Nullable)getOptional:(NSString *_Nonnull)foo error:(NSError *_Nullable *_Nullable)error;

特别注意最后一个参数;因为它是一个错误指针,所以在 swift 中调用此方法需要包装在错误处理程序中(do .. catch)。

这里是对应的.m文件:

+ (NSString *)getOptional:(NSString *)foo error:(NSError *__autoreleasing *)error
{
    if([foo isEqualToString:@"abc"])
    {
        return @"abc item";
    }
    else
    {
        if([foo isEqualToString:@"xyz"])
        {
            *error = [[NSError alloc] init];
        }
        return nil;
    }
}

然后我在我的 swift 文件中添加了以下代码:

func testGetOptional()
{
    do
    {
        var result:NSString? = try OrderItem.getOptional("abc");
        XCTAssertNotNil(result);
        result = try OrderItem.getOptional("123");
        XCTAssertNil(result);

    }
    catch let error as NSError
    {
        XCTFail("OrderItem lookup should not have thrown an error. Error was " + error.localizedDescription);
    }

}

没有什么特别复杂的;对 getOptional 的两次调用实际上都不应该导致错误。但是,当我运行该功能时,“123”案例正在爆炸并导致测试失败。当我仔细观察时,似乎我的objective-c 的桥接版本将返回类型定义为Nonnull (-> OrderItem),即使我在objective-c 中明确将其定义为Nullable。更奇怪的是,如果我在没有最终的“错误”参数的情况下声明相同的函数,那么桥接版本将具有正确的返回类型 Nullable (-> OrderItem?)。

有人能解释一下这里发生了什么吗?更重要的是,有没有办法解决这个问题?

【问题讨论】:

  • 在您的.m 文件中,返回类型是+(NSString *),而不是_Nullable
  • .m文件的返回类型没有区别,swift使用的桥接文件是基于.h文件的。可以肯定的是,我确实尝试将 _Nullable 添加到 .m 但结果是相同的。
  • @pbuchheit:这是意料之中的。带有错误参数的函数被转换为 Swift 作为抛出错误的函数(NULL 返回值表示错误)。请参阅developer.apple.com/library/ios/documentation/Swift/Conceptual/… 中的“错误处理”。
  • 那么有没有办法处理方法可以返回值或抛出我们需要处理的错误的情况?

标签: objective-c swift optional throw bridging-header


【解决方案1】:

在 Cocoa 中存在错误模式,即可能失败的方法将具有返回值和间接错误参数。如果是返回对象引用的方法,则模式为

  • 设置间接错误参数指向的引用,

  • 返回nil

原来是这样的

+(NSString *) getOptional:( NSString *) foo error:(NSError *__autoreleasing *)error
{
   …
   // Error case
   *error = [NSError …];
   return nil;
   …
 }

在 Swift 中,错误被转换为 docatch 构造。因此,从 Swift 的角度来看,永远不会使用返回值 nil 来表示错误,因为执行已被捕获。因此它是不可为空的。

【讨论】:

  • 那么该模式如何与获取请求之类的东西一起使用?如果我正在获取某些东西,那么即使没有抛出任何错误,如果获取根本没有带回任何结果,那么返回 null 也是可能的。
  • 当您有一个实现此错误模式的方法时,返回值nil always 表示错误。如果获取请求想要返回一个空结果集,它会返回一个空数组,而不是nil。 “无集合对象”(又名nil)和“空集合对象”之间存在区别。 Null 不为零。
  • 但情况并非总是如此。例如,我们经常使用 fetch 方法来获取 fetch 结果的第一项。如果结果数组为空,则没有第一项,因此 nil 是唯一有意义的返回值。
  • 我不知道,什么对你有意义。然而,Cocoa 错误模式就是这样。有两种常用的方法来处理可能具有非错误nil 值的情况: a) 为结果设置一个输出参数 (-getFirstItem:error:) 并为信号错误设置一个布尔返回值。 b) 为“无结果”返回一个特殊的返回值。
【解决方案2】:

您不能从使用NSError 指示非错误状态的函数返回nil。如果方法返回 nil,则必须设置 error 指针。

当您使用123 调用它时,这将失败:

// ObjC
+(NSString *) getOptional:( NSString *) foo error:(NSError **)error
{
    if ([foo isEqualToString:@"abc"])
    {
        return @"abc item";
    }
    else if ([foo isEqualToString:@"xyz"])
    {
        NSDictionary * userInfo = @{NSLocalizedDescriptionKey: @"foo cannot be xyz"};
        *error = [NSError errorWithDomain:NSCocoaErrorDomain code:1 userInfo:userInfo];
    }
    else if ([foo isEqualToString:@"123"])
    {
        // 123 is a valid input, but we don't have anything to return
        return nil;
    }

    return nil;
}

// Swift
do {
    let result = try OrderItem.getOptional("123")
} catch let error as NSError {
    print(error.localizedDescription)
}

// Fail:
// The operation couldn’t be completed. (Foundation._GenericObjCError error 0.)

在 ObjC 中调用是没有问题的,但是桥接到 Swift 会导致它失效。相反,您必须返回一个您的应用程序将解释为空的非零值:

// ObjC
else if ([foo isEqualToString:@"123"])
{
    // If it has nothing to return, return an empty string as a token for nothingness
    return [NSString string];
}

// Swift
do {
    let result = try OrderItem.getOptional("123")
    XCTAssert(result.isEmpty, "result must be empty")
} catch let error as NSError {
    XCTFail("OrderItem lookup should not have thrown an error. Error was " + error.localizedDescription);
}

【讨论】:

    猜你喜欢
    • 2017-02-20
    • 1970-01-01
    • 2015-09-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多