大器晩成

탈출 클로저(Escaping Closures) / 자동 클로저 (Autoclosures) 본문

iOS/Swift 문법

탈출 클로저(Escaping Closures) / 자동 클로저 (Autoclosures)

zerobugpark 2025. 2. 28. 16:21

 

[@escaping]

  • 일반적으로 함수의 실행이 종료되면 파라미터로 쓰이는 클로저도 삭제됩니다.
  • 함수에 인수로 전달하지만, 함수가 반환된 후 호출되는 클로저 함수를 탈출(escape)한다라고 말합니다.
  • @escaping 키워드는 클로저를 제거하기 않고 함수에서 탈출시키도록 하며, 클로저가 함수의 실행 흐름을(스택프레임)을 벗어날 수 있도록 합니다. 

 

 non - escaping clousre

  • 함수내부에서, 단순하게 실행하고 종료할 때 (클로저를 힙에 저장할 필요가 없습니다.)
func hello(clousre: (String) -> ()) {
    
    let msg = "Hello Swift"
    clousre(msg)
    
}

hello { msg in
    print(msg)
}
  • 일반적으로 함수의 실행이 종료되면 클로저는 삭제됩니다.

 escaping clousre

  • 함수의 실행을 벗어나도 사용이 필요한 경우 (힙에 저장되기 때문에, 메모리 관리가 필요함)
var squareFunction: (Int) -> () = {
    print("\($0) * \($0) = \($0 * $0) 입니다. ")
}

squareFunction(10)  // 10 * 10 = 100 입니다. 

func square(closure: @escaping (Int) -> ()) {
    squareFunction = closure // 클로저에 실행이 아닌, squareFunction 함수에 저장
}

square {
    print("\($0)의 제곱은  = \($0 * $0) 입니다. ")
}



squareFunction(10) // 10의 제곱은  = 100 입니다.
  • square함수를 호출할 때는 아무 일도 일어나지 않습니다.
  • squareFunction 실제 외부 변수를 실행해야 동작합니다.

 @escaping 사용의 대표적인 경우

  • 어떤 함수의 내부에 존재하는 클로저(함수)를 외부 변수에 저장할 때
  • GCD(비동키 코드를 사용할 때)

 

[@autoclosure]

  • autoclousre는 함수에 인수로 전달되는 표현식을 래핑 하기 위해 자동으로 생성되는 클로저입니다. 인수를 가지지 않으며 호출될 때 내부에 래핑 된 표현식의 값을 반환합니다.
  • 일반적으로 클로저 형태로 사용해도 되지만, 너무 번거로울 때 사용됩니다.
  • 번거로움을 해결해 줄 수는 있지만, 실제 코드가 명확해 보이지 않아 사용을 지양하는 편입니다(애플 공식문서)
func even(closure: @autoclosure () -> Bool) {
    if closure() {
        print("짝수입니다.")
    } else {
        print("홀수입니다.")
    }
}

//even(closure: Bool)
even(closure: 10 % 2 == 0)

 

 

autoclosure는 기본적으로 non-escaping 특성을 가지고 있습니다.

  • autoclosure가 이스케이프를 허용하길 원한다면 @autoclousre와 @escaping 속성을 둘 다 사용하면 됩니다.
// Apple 공식문서 예제

var customersInLine = ["Barry", "Daniella"]

// customersInLine is ["Barry", "Daniella"]
var customerProviders: [() -> String] = []
func collectCustomerProviders(_ customerProvider: @autoclosure @escaping () -> String) {
    customerProviders.append(customerProvider)
}
collectCustomerProviders(customersInLine.remove(at: 0))
collectCustomerProviders(customersInLine.remove(at: 0))

print("Collected \(customerProviders.count) closures.")
// Prints "Collected 2 closures."
for customerProvider in customerProviders {
    print("Now serving \(customerProvider())!")
}
// Prints "Now serving Barry!"
// Prints "Now serving Daniella!"

print(customersInLine) // []
  • 위의 코드에서 customerProvider 인수로 전달된 클로저를 호출하는 대신에 collectCustomerProviders(_:) 함수는 클로저를 customerProviders 배열에 추가합니다. 이 배열은 함수의 범위 밖에 선언됩니다. 이것은 배열에 클로저는 함수가 반환된 후에 실행될 수 있다는 의미입니다. 그 결과 customerProvider 인수의 값은 함수의 범위를 벗어날 수 있어야 합니다.
728x90