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


139

프로토콜이 있습니다.

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에서는 작동하지 않습니다. 근본적인 원인을 알려줄 수 있습니까?


5
이것은 Swift 3
Honey

1
우리가 이것을해야한다는 것은 말이되지 않습니다. 다른 언어는 필요하지 않습니다.
Andrew Koster 1

답변:


243

이는 함수 유형의 매개 변수에 대한 기본 동작이 변경 되었기 때문입니다. 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 제안 을 참조하십시오 .


2
그러나 탈출을 허용하지 않는 클로저를 어떻게 사용합니까?
Eneko Alonso

6
@EnekoAlonso 요구하는 내용을 완전히 확신하지 못합니다. 이스케이프가 아닌 함수 매개 변수를 함수 자체에서 직접 호출하거나 이스케이프가 아닌 클로저에서 캡처 할 때 호출 할 수 있습니다. 이 경우 비동기 코드를 처리 할 때 종료 전에 async함수 매개 변수 (및 completion함수)가 호출 될 것이라는 보장이 fetchData없으므로 반드시 있어야합니다 @escaping.
Hamish

프로토콜의 메소드 서명으로 @escaping을 지정해야한다고 생각되는 것 같습니다. 우리가해야 할 일입니까? 제안은 말하지 않습니다! : S
Sajjon

1
@Sajjon 현재, @escaping프로토콜 요구 사항의 @escaping매개 변수를 해당 요구 사항 구현의 매개 변수 와 일치 시켜야합니다 (비 탈출 매개 변수의 경우도 마찬가지). Swift 2에서도 동일했습니다 @noescape.
Hamish


30

@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)
}
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.