我有一个协议:
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) } } }
上面的代码已在Swift3(Xcode8-beta5)中编译并工作,但不再与beta 6一起工作。您能指出我的根本原因吗?
这是由于功能类型参数的默认行为发生了变化。在Swift 3(特别是Xcode 8 beta 6附带的内部版本)之前,它们将默认转义- 您必须对其进行标记@noescape,以防止它们被存储或捕获,从而确保它们不会超过使用寿命函数调用。
@noescape
但是,现在@noescape是函数类型参数的默认设置。如果要存储或捕获此类功能,则现在需要对其进行标记@escaping:
@escaping
protocol DataServiceType { func fetchData(location: String, completion: **@escaping** (DataFetchResult) -> Void) func cachedData(location: String) -> Data? }
func fetchData(location: String, completion: **@escaping** (DataFetchResult) -> Void) { // ... }
有关此更改的更多信息,请参见Swift Evolution提案。