프로토콜이 있습니다.
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)에서 컴파일되고 작동했지만 더 이상 베타 6에서는 작동하지 않습니다. 근본적인 원인을 알려줄 수 있습니까?
답변
이는 함수 유형의 매개 변수에 대한 기본 동작이 변경 되었기 때문입니다. 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 제안 을 참조하십시오 .
답변
@noescape가 기본값이므로 오류를 수정하는 두 가지 옵션이 있습니다.
1) @Hamish가 그의 대답에서 지적했듯이 결과에 관심이 있고 실제로 탈출하기를 원한다면 완료를 @escaping으로 표시하십시오 (아마도 Unit Tests에 대한 @Lukasz의 질문의 예 및 비동기 가능성) 완성)
func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void)
또는
2) 결과에 신경 쓰지 않는 경우 결과를 완전히 버리고 선택 사항으로 완료를 선택하여 기본 @noescape 동작을 유지하십시오. 예를 들어, 사용자가 이미 “지워”있고 부주의 한 네트워크 호출이 있었기 때문에 호출 뷰 컨트롤러가 메모리에 멈출 필요가없는 경우. 내가 여기에 왔을 때 대답을 찾고 샘플 코드가 나에게 관련성이 없었으므로 @noescape를 표시하는 것이 첫 번째 옵션에서 유일한 것으로 들렸지 만 최선의 선택은 아니 었습니다.
func fetchData(location: String, completion: ((DataFetchResult) -> Void)?) {
...
completion?(self.result)
}