5

我正在开发一个需要与现有的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?)。

有人可以阐明这里发生了什么吗?更重要的是,有没有办法解决这个问题?

4

2 回答 2

4

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

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

  • 返回nil

所以看起来像这样

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

在 Swift 中,错误被翻译成一个do...catch构造。nil因此,从 Swift 的角度来看,永远不会使用发出错误信号的返回值,因为执行已被捕获。因此它是不可为空的。

于 2015-12-09T20:33:27.900 回答
0

您不能从用于指示非错误状态nil的函数返回。NSError如果方法返回 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);
}
于 2015-12-10T04:29:28.630 回答