[swift] 비 이스케이프 매개 변수를 닫으면 이스케이프 될 수 있습니다.

프로토콜이 있습니다.

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)
}


답변