プロトコルがあります:
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 では動作しません。根本的な原因を教えていただけますか?
ベストアンサー1
これは、関数型のパラメータのデフォルトの動作が変更されたためです。Swift 3 より前 (具体的には、Xcode 8 ベータ 6 に同梱されているビルド) では、デフォルトでエスケープされていました。つまり、@noescape
保存またはキャプチャされないようにマークする必要があり、これにより、関数呼び出しの期間を超えて存続しないことが保証されます。
ただし、@noescape
関数型パラメータの場合は がデフォルトになりました。このような関数を保存またはキャプチャする場合は、 をマークする必要があります@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 提案この変更の詳細については、こちらをご覧ください。