当完成处理程序显式使用@escaping

时间:2019-03-05 21:18:09

标签: swift closures swift4 completionhandler

Swift 4.2,Xcode 10.1

在我正在使用的订单处理应用中,用户可以搜索已经处理或提交的订单。发生这种情况时,它将检查是否有订单缓存,如果没有,将使用异步API请求重新填充该缓存,然后再次检查该缓存。

重新填充缓存的函数是一个私有静态函数,它接受转义的完成处理程序。每当我过去使用该完成处理程序时,我要做的就是在函数调用的末尾添加一个闭包。这是在指示我尽可能对所有数据进行缓存之前,仅使用API​​重新填充该缓存之前。从那时起,该函数就变得私有了,因为不再需要从此类之外的任何地方直接调用API。

现在,当我将闭包直接放在函数调用之后时,这给了我一个错误,该错误基本上表示我正在传递@nonescaping闭包而不是@escaping闭包:

"Cannot invoke 'getAndCacheAPIData' with an argument list of type '(type: Codable.Type, (String?) -> Void)', Expected an argument list of type '(type: CodableClass.Type, @escaping (String?) -> Void)'"

我以前从未必须将闭包声明为@转义,这似乎是可能的。我怀疑由于该函数既是私有的又是静态的,所以推断闭包被@escaping的方式会发生某种问题。我不懂事我可以尝试将静态类转换为单例,但是由于一个错误,我很犹豫地重构一堆工作代码,直到我绝对确定更改将解决问题为止,而我要做的是除非我改变方法,否则不可能。

代码如下:

public static func fillSearchResultArray<ManagedClass: NSManagedObject>(query:String, parameters:[String], with type: ManagedClass.Type, completionHandler: @escaping (String?)->Void)
{
    let codableType:Codable.Type
    switch type
    {
        case is ClientTable.Type:
            codableType = ClientData.self
        case is OrderTable.Type:
            codableType = OrderData.self
        case is ProductTable.Type:
            codableType = ProductData.self
        default:
            completionHandler("Unrecognized type.")
            return
    }
    let fetchedData:[ManagedClass]
    do
    {
        fetchedData = try PersistenceManager.shared.fetch(ManagedClass.self)
    }
    catch
    {
        completionHandler(error.localizedDescription)
        return
    }

    if fetchedData.isEmpty
    {
        AppNetwork.getAndCacheAPIData(type: codableType)//error here
        {(firstErrorString) in
            //move search array data to the cache
            if firstErrorString.exists
            {
                completionHandler(error)
            }
            else
            {
                AppNetwork.fillSearchResultArray(query: query, parameters: parameters, type: type)
                { errorString in
                    completionHandler(errorString)
                }
            }
        }

        return
    }
    else
    { ...

被调用函数的签名:

private static func getAndCacheAPIData <CodableClass: Any & Codable>(type:CodableClass.Type, completionHandler: @escaping (String?)->Void)

为什么在总是将其关闭推断为@escapeing之前,迅速将其视为默认的@nonescaping?

1 个答案:

答案 0 :(得分:1)

问题与闭包,静态或私有无关。它与类型参数有关。您不能调用此方法:

private static func getAndCacheAPIData <CodableClass: Any & Codable>(type:CodableClass.Type, completionHandler: @escaping (String?)->Void)

,其变量类型为Codable.Type。您传递的类型值必须是在编译时已知的具体类型。如果要传递变量,则不能使用泛型。必须是:

private static func getAndCacheAPIData(type: Codable.Type, completionHandler: @escaping (String?)->Void)

或者,您可以将其称为:

 AppNetwork.getAndCacheAPIData(type: Int.self) {(firstErrorString) in ... }

或其他一些编译时已知的类型。

您可能真正想要的是这样的东西:

let completion: (String?) -> Void = {(firstErrorString) in ... }

switch ... {
    case ...:
        AppNetwork.getAndCacheAPIData(type: Int.self, completion: completion)
    case ...:
        AppNetwork.getAndCacheAPIData(type: String.self, completion: completion)
    ...

基本问题是协议不符合自身,因此类型Codable.Type的变量不能满足: Codable的要求。这归结为您不能打电话给的相同原因:

AppNetwork.getAndCacheAPIData(type: Codable.self) {...}

或者,您可以这样重构:

private static func handleAPI<CodableClass: Codable>(type: CodableClass.Type) {
    getAndCacheAPIData(type: type.self) { _ in ... the completion handler ..}
}


switch ... {
    case ...:
        AppNetwork.handleAPI(type: Int.self)
    case ...:
        AppNetwork.handleAPI(type: String.self)
    ...

旁注:Any &在这里毫无意义。您只是说<CodableClass: Codable>