代码之家  ›  专栏  ›  技术社区  ›  A. L. Strine

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

  •  0
  • A. L. Strine  · 技术社区  · 6 年前

    斯威夫特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?

    0 回复  |  直到 6 年前
        1
  •  1
  •   Rob Napier    6 年前

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

    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 要求。这和你不能直接打电话的原因是一样的:

    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> .