使用 Objective-C 函数 Returns 一个可选的或投入 Swift
Using An Objective-C Function that Returns an Optional or Throw in Swift
我正在开发一个 swift 项目,该项目需要与现有的 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 的桥接版本似乎将 return 类型定义为 Nonnull (-> OrderItem),即使我在 [=30= 中将其明确定义为 Nullable ].更奇怪的是,如果我在没有最终 'error' 参数的情况下声明相同的函数,那么桥接版本将具有正确的 return 类型的 Nullable (-> OrderItem?).
有人可以阐明这里发生的事情吗?更重要的是,有什么办法可以解决这个问题吗?
在Cocoa中有一个错误模式,一个可能失败的方法将有一个return值和一个间接错误参数。如果它是一个方法 returning 一个对象引用,模式是
设置间接错误参数指向的引用,
return nil
看起来像这样
+(NSString *) getOptional:( NSString *) foo error:(NSError *__autoreleasing *)error
{
…
// Error case
*error = [NSError …];
return nil;
…
}
在 Swift 中,错误被翻译成 do
…catch
结构。因此,从 Swift 的角度来看,永远不会使用 nil
的 return 值表示错误,因为执行被捕获了。因此它是不可为空的。
您不能从使用 NSError
指示非错误状态的函数中 return nil
。如果方法 return 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的桥接使其无效。相反,您必须 return 一个您的应用程序将解释为空的非零值:
// 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);
}
我正在开发一个 swift 项目,该项目需要与现有的 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 的桥接版本似乎将 return 类型定义为 Nonnull (-> OrderItem),即使我在 [=30= 中将其明确定义为 Nullable ].更奇怪的是,如果我在没有最终 'error' 参数的情况下声明相同的函数,那么桥接版本将具有正确的 return 类型的 Nullable (-> OrderItem?).
有人可以阐明这里发生的事情吗?更重要的是,有什么办法可以解决这个问题吗?
在Cocoa中有一个错误模式,一个可能失败的方法将有一个return值和一个间接错误参数。如果它是一个方法 returning 一个对象引用,模式是
设置间接错误参数指向的引用,
return
nil
看起来像这样
+(NSString *) getOptional:( NSString *) foo error:(NSError *__autoreleasing *)error
{
…
// Error case
*error = [NSError …];
return nil;
…
}
在 Swift 中,错误被翻译成 do
…catch
结构。因此,从 Swift 的角度来看,永远不会使用 nil
的 return 值表示错误,因为执行被捕获了。因此它是不可为空的。
您不能从使用 NSError
指示非错误状态的函数中 return nil
。如果方法 return 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的桥接使其无效。相反,您必须 return 一个您的应用程序将解释为空的非零值:
// 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);
}