비 이스케이프 매개 변수의 클로저 사용으로 인해 이스케이프 될 수 있음
프로토콜이 있습니다.
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) {
// ...
}
See the Swift Evolution proposal for more info about this change.
Since @noescape is the default, there 2 options to fix the error:
1) as @Hamish pointed out in his answer, just mark the completion as @escaping if you do care about the result and really want it to escape (that probably is the case in @Lukasz's question with Unit Tests as example and possibility of async completion)
func fetchData(location: String, completion: @escaping (DataFetchResult) -> Void)
OR
2) keep the default @noescape behavior by making the completion optional discarding the results altogether in cases when you don't care about the result. For example when user has already "walked away" and the calling view controller doesn't have to hang in memory just because there was some careless network call. Just as it was in my case when I came here looking for answer and the sample code wasn't very relevant for me, so marking @noescape was not the best option, event though it sounded as the only one from the first glance.
func fetchData(location: String, completion: ((DataFetchResult) -> Void)?) {
...
completion?(self.result)
}
'Programing' 카테고리의 다른 글
오류 : "삽입 할 노드가 다른 문서 컨텍스트에 있습니다." (0) | 2020.08.18 |
---|---|
Django 1.7의 초기 마이그레이션에서 다시 마이그레이션하는 방법은 무엇입니까? (0) | 2020.08.18 |
Scala에서 컴패니언 객체를 갖는 이유는 무엇입니까? (0) | 2020.08.18 |
MySQL : 트랜잭션 대 잠금 테이블 (0) | 2020.08.18 |
MySQL에 쓸 때 TextArea에서 줄 바꿈 유지 (0) | 2020.08.18 |