programing

Swift @escape 및 완료 핸들러

sourcejob 2023. 8. 7. 22:31
반응형

Swift @escape 및 완료 핸들러

저는 스위프트의 'Closure'를 좀 더 정확하게 이해하려고 합니다.

그렇지만@escaping그리고.Completion Handler이해하기가 너무 어렵습니다.

많은 스위프트 게시물과 공식 문서를 검색해봤지만 아직 부족함을 느꼈습니다.

다음은 공식 문서의 코드 예제입니다.

var completionHandlers: [()->Void] = []

func someFunctionWithEscapingClosure(completionHandler: @escaping ()->Void){
    completionHandlers.append(completionHandler)
}

func someFunctionWithNoneescapingClosure(closure: ()->Void){
    closure()
}

class SomeClass{
    var x:Int = 10
    func doSomething(){
        someFunctionWithEscapingClosure {
            self.x = 100
            //not excute yet
        }
        someFunctionWithNoneescapingClosure {
            x = 200
        }
    }
}

let instance = SomeClass()
instance.doSomething()
print(instance.x)

completionHandlers.first?() 
print(instance.x)

사용하는 방법과 이유는 두 가지가 있다고 들었습니다.@escaping

첫 번째는 폐쇄를 저장하는 것이고, 두 번째는 비동기 운영을 위한 것입니다.

다음은 저의 질문입니다.

첫째, 만약doSomething실행 시someFunctionWithEscapingClosure폐쇄 매개 변수를 사용하여 실행하고 해당 폐쇄는 전역 변수 배열에 저장됩니다.

마감은 {self.x = 100}인 것 같습니다.

어떻게.self글로벌 변수에 저장된 {self.x = 100}에서completionHandlers에 연결할 수 있습니다.instance의 그 목적.SomeClass?

둘째, 이해합니다.someFunctionWithEscapingClosure이것처럼.

로컬 변수 폐쇄를 저장하는 방법completionHandler전역 변수 'completionHandler'에 연결we using@키워드!

없이.@escaping키워드someFunctionWithEscapingClosure반환, 로컬 변수completionHandler메모리에서 제거합니다.

@escaping그 폐쇄를 기억 속에 간직하는 것입니다.

이게 맞나

마지막으로 이 문법의 존재가 궁금합니다.

아마도 이것은 매우 초보적인 질문일 것입니다.

우리가 어떤 특정한 기능 이후에 어떤 기능을 실행하기를 원한다면요.특정 함수 호출 후에 일부 함수를 호출하는 것이 어떻습니까?

위의 패턴을 사용하는 것과 이스케이프 콜백 기능을 사용하는 것의 차이점은 무엇입니까?

신속 완료 핸들러 이스케이프 및 비 이스케이프:

사용자가 앱을 사용하는 동안 앱을 업데이트한다고 가정합니다.작업이 완료되면 사용자에게 통지해야 합니다.여러분은 아마도 "축하합니다, 이제 충분히 즐길 수 있습니다!"라고 적힌 상자를 띄우고 싶을 것입니다.

그렇다면 다운로드가 완료된 후에만 코드 블록을 실행하는 방법은 무엇입니까?또한 뷰 컨트롤러를 다음으로 이동한 후에만 특정 개체를 애니메이션화하는 방법은 무엇입니까?자, 우리는 상사처럼 디자인하는 방법에 대해 알아볼 것입니다.

나의 광범위한 어휘 목록에 따르면, 완성 처리기는 다음을 의미합니다.

작업이 완료되면 작업 수행

Bob의 게시물은 완료 핸들러에 대한 명확성을 제공합니다(개발자의 관점에서 이해해야 할 사항을 정확히 정의함).

@잠금식 폐쇄:

함수 인수에서 클로저를 통과하면 함수의 본문이 실행되고 컴파일러가 반환된 후 이를 사용합니다.함수가 종료되면 종료가 실행될 때까지 전달된 종료의 범위가 존재하고 메모리에 존재합니다.

기능을 포함하는 폐쇄를 피하는 몇 가지 방법이 있습니다.

  • 저장소:글로벌 변수에 클로저를 저장해야 하는 경우 호출 함수의 메모리 과거에 존재하는 속성 또는 다른 저장소가 실행되고 컴파일러를 다시 반환합니다.

  • 비동기 실행:발송 대기열에서 마감을 비동기식으로 실행하는 경우, 대기열은 마감을 메모리에 저장하여 나중에 사용할 수 있습니다.이 경우 마감이 언제 실행될지 알 수 없습니다.

이러한 시나리오에서 폐쇄를 사용하려고 하면 Swift 컴파일러에 다음 오류가 표시됩니다.

error screenshot

이 주제에 대한 자세한 내용은 Medium에서 이 게시물을 확인하십시오.

모든 iOS 개발자가 이해해야 할 점을 하나 더 추가합니다.

  1. 이스케이프 클로저 : 이스케이프 클로저는 함수가 반환된 후에 호출되는 클로저입니다.즉, 전달된 기능보다 오래 지속됩니다.
  2. 비탈출 폐쇄 : 전달된 함수 내에서 호출되는 폐쇄입니다. 즉, 반환되기 전에 종료됩니다.

여기 제가 @escape가 어떻게 작동하는지 스스로에게 상기시키기 위해 사용하는 작은 클래스의 예가 있습니다.

class EscapingExamples: NSObject {

    var closure: (() -> Void)?

    func storageExample(with completion: (() -> Void)) {
        //This will produce a compile-time error because `closure` is outside the scope of this
        //function - it's a class-instance level variable - and so it could be called by any other method at
        //any time, even after this function has completed. We need to tell `completion` that it may remain in memory, i.e. `escape` the scope of this
        //function.
        closure = completion
        //Run some function that may call `closure` at some point, but not necessary for the error to show up.
        //runOperation()
    }

    func asyncExample(with completion: (() -> Void)) {
        //This will produce a compile-time error because the completion closure may be called at any time
        //due to the async nature of the call which precedes/encloses it.  We need to tell `completion` that it should
        //stay in memory, i.e.`escape` the scope of this function.
        DispatchQueue.global().async {
            completion()
        }
    }

    func asyncExample2(with completion: (() -> Void)) {
        //The same as the above method - the compiler sees the `@escaping` nature of the
        //closure required by `runAsyncTask()` and tells us we need to allow our own completion
        //closure to be @escaping too. `runAsyncTask`'s completion block will be retained in memory until
        //it is executed, so our completion closure must explicitly do the same.
        runAsyncTask {
            completion()
        }
    }





    func runAsyncTask(completion: @escaping (() -> Void)) {
        DispatchQueue.global().async {
            completion()
        }
    }

}
/*the long story short is that @escaping means that don't terminate the function life time until the @escaping closure has finished execution in the opposite of nonEscaping closure the function can be terminated before the closure finishes execution Ex:
*/

 func fillData(completion: @escaping: () -> Void){ 
     /// toDo 
    completion()
  }

//___________________________

//The call for this function can be in either way's @escaping or nonEscaping :

    
fillData{
 /// toDo
}
    

/* again the deference between the two is that the function can be terminated before finish of execution nonEscaping closure in the other hand the @escaping closure guarantees that the function execution will not be terminated before the end of @escaping closure execution. Hope that helps ***#(NOTE THAT THE CLOSURE CAN BE OF ANY SWIFT DATA TYPE EVEN IT CAN BE TYPEALIAS)*/
 

언급URL : https://stackoverflow.com/questions/46245517/swift-escaping-and-completion-handler

반응형