当完成处理程序显式使用@escaping时,Swift推断完成处理程序闭包是默认的@nonescaping而不是@escaping

问题描述 投票:0回答:1

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,这似乎是可能的。我怀疑因为该函数既是私有的又是静态的,所以在闭包推断为@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)

为什么swift推断这个闭包是默认的@nonescaping,之前它总是推断它是@escaping?

swift closures swift4 completionhandler
1个回答
1
投票

这个问题与闭包,静态或私有无关。它与type参数有关。你无法调用这个方法:

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>

© www.soinside.com 2019 - 2024. All rights reserved.