如何以@noescap的方式在Swift4中的Block中使用Object-C函数?

时间:2019-01-29 02:12:30

标签: objective-c swift

我正在Swift4中使用名为“ YapDatabase”的Object-C框架开发iOS应用。类'YapDatabaseConnection'中有一个带有这样一个块的Object-C函数:

setdiff(y, x)

我以这种方式使用该功能:

- (void)readWithBlock:(void (^)(YapDatabaseReadTransaction *transaction))block;

我认为在声明“ events”参数后将立即执行关闭。实际上,闭包不会在结果返回之前执行。为了查找原因,我打开了Xcode生成的名为“ YapDatabaseConnection.h(Interface)”的文件(使用cmd + shift + o),发现该函数已通过以下方式转换为Swift:

static func readNovelIds() -> [String]? {        
    let account = XFAccountManager.share().account
    var events: [XFNovelClickEvent]?
    OTRDatabaseManager.shared.readOnlyDatabaseConnection?.read({ (transaction) in
        events = XFNovelClickEvent.allNovelClickEvents(accountId: account.uniqueId, transaction: transaction)
    })
    guard let clickEvents = events else {
        return nil
    }
    let readNovelsIds = clickEvents.map {
        $0.bookId ?? ""
    }
    return readNovelsIds
}

那么,如何以@noescap方式使用此功能?

3 个答案:

答案 0 :(得分:0)

作为调用者,执行闭包时不能更改。这取决于read()函数。如果控制该函数,则需要对其进行修改以立即调用闭包。如果您无法控制它,那么您将无法修改其行为。

您可以使用Waiting until the task finishes中所述的DispatchGroup将异步呼叫转换为同步呼叫。但是,您不能在主队列上进行数据库调用。您可能会崩溃该应用程序。通常,在这种情况下,您应该只使用异步调用(即,使readNovelIds也异步并采用完成处理程序)。

答案 1 :(得分:0)

Xcode将Objective-C块桥接为@escaping的原因是因为该块可能在函数返回后执行。

由于您不拥有YapDatabase,因此无法修改源代码以使其不转义,因此您可能希望使readNovelIds函数将闭包作为参数并传递给通过闭包返回值。

static func readNovelIds(resultHandler: @escaping ([String]?) -> ()) {
    let account = XFAccountManager.share().account
    var events: [XFNovelClickEvent]?
    OTRDatabaseManager.shared.readOnlyDatabaseConnection?.read({ (transaction) in
        events = XFNovelClickEvent.allNovelClickEvents(accountId: account.uniqueId, transaction: transaction)
        if let clickEvents = events {
            let readNovelsIds = clickEvents.map {
                $0.bookId ?? ""
            }
            resultHandler(readNovelsIds)
        }
        resultHandler(nil)
    })
}

答案 2 :(得分:0)

如果该方法实际上是同步的(即它将不允许该块转义其上下文),则应该使用NS_NOESCAPE装饰Objective C标头方法。查看文档(确实说它是同步的)和实现,应该用这种方式对其进行注释。

- (void)readWithBlock:(void (NS_NOESCAPE ^)(YapDatabaseReadTransaction *transaction))block;

我认为,这应该允许Swift接口导入器添加@noescaping声明。您可能应该在YapDatabase项目上提交错误请求;他们可以在那里更改它。