我有一个协议:
enum DataFetchResult {
case success(data: Data)
case failure
}
protocol DataServiceType {
func fetchData(location: String, completion: (DataFetchResult) -> (Void))
func cachedData(location: String) -> Data?
}
通过示例实现:
/// An implementation of DataServiceType protocol returning predefined results using arbitrary queue for asynchronyous mechanisms.
/// Dedicated to be used in various tests (Unit Tests).
class DataMockService: DataServiceType {
var result : DataFetchResult
var async : Bool = true
var queue : DispatchQueue = DispatchQueue.global(qos: .background)
var cachedData : Data? = nil
init(result : DataFetchResult) {
self.result = result
}
func cachedData(location: String) -> Data? {
switch self.result {
case .success(let data):
return data
default:
return nil
}
}
func fetchData(location: String, completion: (DataFetchResult) -> (Void)) {
// Returning result on arbitrary queue should be tested,
// so we can check if client can work with any (even worse) implementation:
if async == true {
queue.async { [weak self ] in
guard let weakSelf = self else { return }
// This line produces compiler error:
// "Closure use of non-escaping parameter 'completion' may allow it to escape"
completion(weakSelf.result)
}
} else {
completion(self.result)
}
}
}
上面的代码在Swift 3(Xcode 8-beta5)中编译并运行,但在beta 6中不再运行。你能告诉我潜在的原因吗?
3条答案
按热度按时间2w3kk1z51#
这是由于函数类型参数的默认行为发生了变化,在Swift 3之前(特别是Xcode 8 beta 6附带的版本),它们默认为转义--你必须将它们标记为
@noescape
,以防止它们被存储或捕获,这保证了它们不会超过函数调用的持续时间。但是,现在
@noescape
是函数类型参数的默认值,如果要存储或捕获这样的函数,现在需要将其标记为@escaping
:有关此更改的更多信息,请参见Swift Evolution proposal。
luaexgnf2#
由于@noescape是默认值,因此有两个选项可以修复错误:
1)正如@Hamish在他的回答中指出的,如果你确实关心结果并且真的希望它逃逸,就把完成标记为@ escape(这可能是@Lukasz的问题中的情况,单元测试是一个例子,异步完成的可能性也是如此)
或
2)保留默认的@noescape行为,方法是在您不关心结果的情况下,将完成操作设置为可选并完全丢弃结果。例如,当用户已经“走开”,调用视图控制器不必因为某个粗心的网络调用而挂起。就像我的情况一样,当我来到这里寻找答案时,示例代码对我来说不是很相关。因此,标记@noescape并不是最佳选择,尽管乍看之下似乎是唯一的选择。
wnrlj8wa3#
将完成块作为
optional
变量帮助我解决了Swift 5中的问题。其中,数据存储库的成功为
@escaping