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?
答案 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>
。