在 Swift 中使用 Obj-C 完成块

Posted

技术标签:

【中文标题】在 Swift 中使用 Obj-C 完成块【英文标题】:Using Obj-C completion block in Swift 【发布时间】:2016-05-20 22:15:22 【问题描述】:

在 Objective-C 中,我有一个完成块类定义为:

文件.h

typedef void (^MYCompletionBlock)(BOOL success, NSDictionary *result, NSError *error);

然后,在一个 Swift 文件中,我尝试如下使用完成块:

Swift.swift

class MyClass: NSObject
     ...

     func MyFunction() -> Void 
          ...
          objcMethod(param1, withCompletion: (MYCompletionBlock) -> Void in
               if (success) // Error:"Use of unresolved identifier 'success'"
               
          
          ... 
     
     ...

但是,我不断收到错误消息:“使用未解析的标识符‘成功’”。

我也尝试了以下方法:

objcMethod(param1, withCompletion: (success:Bool, result: NSDictionary, error:NSError) -> Void in
     if (success) // Error:"Cannot convert value of type '(Bool, NSDictionary, NSError) -> Void' to expected argument type "MYCompletionBlock!" 
     

有人可以帮我理解如何在 Swift 中正确指定 Obj-C 完成块吗?

【问题讨论】:

Obj-C 方法声明是什么样的? 【参考方案1】:

鉴于您的闭包没有指定可空性限定符(它们几乎可以肯定是可选的),因此可以放心地假设您的 Objective-C API 没有经过可空性审核。因此,Swift 会将指针视为隐式展开的可选项。此外,现在NSDictionary 映射到[NSObject : AnyObject] Swift 字典。

因此,它将是:

obj.objcMethod(param)  (success: Bool, result: [NSObject : AnyObject]!, error: NSError!) in
    if success 
        // do something
    

或者,正如 Kobi 指出的那样,您可以让编译器推断类型:

obj.objcMethod(param)  success, result, error in
    if success 
        // do something
    

请注意,您不必自己记住这一点。您可以在输入代码时利用 Xcode 的代码完成功能。所以,输入足够的匹配方法名称,当它匹配objcMethod,然后按回车:

当你到达MYCompletionBlock时,再次按回车,它会显示正确的签名:


如果这个 Objective-C 方法是我自己的类,我会审核它的可空性。因此,例如,假设param 是可选的,闭包是必需的,而resulterror 是可选的,您可以这样定义:

NS_ASSUME_NONNULL_BEGIN

typedef void (^MYCompletionBlock)(BOOL success, NSDictionary * _Nullable result, NSError * _Nullable error);

@interface MyObject : NSObject

- (void)objcMethod:(NSDictionary * _Nullable)param1 withCompletionHandler:(MYCompletionBlock)completionHandler;

@end

NS_ASSUME_NONNULL_END

如果是这样的话,你的 Swift 代码会这样称呼它:

obj.objcMethod(param)  (success: Bool, result: [NSObject : AnyObject]?, error: NSError?) in
    if success 
        // do something
    

或者,再次让编译器为您推断类型(但这次它们将被推断为未隐式解包的可选项):

obj.objcMethod(param)  success, result, error in
    if success 
        // do something
    

【讨论】:

实际上,我只是注意到使用 Xcode 的自动完成,我得到了以下信息:obj.objcMethod(param1, withCompletion: (success, result, error) -> Void in if (success) // this worked! 这在接受的解决方案中有效,实际上我无法进入完成块。关于为什么要@Rob 的想法? @Vee - 这可能与 Objective-C 方法的实现方式有关(OP 没有详细说明,因此可能会有一些变化)。它可能是 Xcode 的版本(我使用的是 7.3.1)。这可能是很多事情。但是,如果您的演绎有效,objc.objcMethod(param1) success, result, error in if success ... 也可以。 (显式声明类型的版本完全取决于方法最初是如何声明的。)但底线是,如果您让自动完成完成其工作,您将获得一个适用于您的方法和环境的版本。 【参考方案2】:

您不应该为完成块参数指定类型,因为某些类型在 Swift 和 Objective C 之间存在延迟(例如,BOOL 在 Swift 中实际上是 ObjCBool)。 这应该有效:

objcMethod(param1)  (success, result, error) in
    if (success) 
        // Do something
    

【讨论】:

谢谢,科比。您的解决方案完美运行。我在下面接受了 Rob 的回答,因为他展示了有关 Xcode 代码完成功能如何帮助我得出正确答案的更多细节。

以上是关于在 Swift 中使用 Obj-C 完成块的主要内容,如果未能解决你的问题,请参考以下文章

swift中属性的完成处理程序

请求音乐库权限 swift 或 obj-c

Swift - 从完成块中解除视图控制器

如何检测UIScrollView何时完成滚动在Swift中

完成块在 Swift 3 中变为 nil

NSURLSession,完成块,Swift