Swift

Swift | 클로저(Closure)에 관하여 -2

ziziDev 2024. 5. 21. 22:25
반응형

안녕하세요! 오늘은

클로저 2편으로 왔습니다

 

정리할 부분이 많기 때문에

또 다시 힘차게 정리를 해보겠습니다!

 

저번시간에 클로저를 함수의 파라미터에

넣을 수 있는 부분을 마지막으로 끝을 냈습니다

 

거기서 더하여

예시를 좀 더 보려고 합니다

 

콜백함수

 

다른 언어에서도 많이 사용하는 람다 형식의 콜백함수를 볼 수 있습니다

 

만약 호출할 때 형식의 어려움이 있다면?

 

엔터를 치면 바로 형태가 쉽게 나온답니다

엔터의 결과를 아직 보지 못했으니 아래에

한 번 더 후행 클로저에 대해서 설명할 예정이니 계속 해서 읽어주세요!

 

클로저를 간소화 하는 방법은 많습니다

 

문맥상에서 파라미터와 반환벨류(return value) 타입 추론

한 줄의 코드(싱글 익스프레션)인 경우에 리턴을 적지 않아도 됩니다

아규먼트 이름을 축약이 가능합니다 : $0, $1

트레일링 클로저문법으로 함수의 마지막 전달 인자(아규먼트)로 클로저가 전달되는 경우에

소괄호를 생략이 가능한 후행클로저 문법을 실행하고 있습니다

 

후행클로저 문법을

보면서 다른 것들도 함께 

다루도록 하겠습니다

 

후행 클로저

|

Trailing  Closures

 

이렇게 마지막이 함수 파라미터 전달 인자가 클로저로 전달되는 경우는

소괄호 생략이 가능하답니다

 

 

엔터 한 번에 놀라운 일이 벌어졌죠?

 

 

 

이렇게 사용할 수 있답니다 

 

 

타입추론도 웬만하면 가능하기 때문에

리턴을 작성하지 않아도 되는걸 볼 수 있습니다

 

아규먼트 축약에 대해서도 볼 수 있습니다

첫 번째 파라미터 두 번째 파라미터를 굳이 작성하지 않아도

$0, $1로 작성을 할 수 있습니다

 

$0 첫 번째 파라미터

$1 두 번째 파라미터

라는 뜻 입니다

 

이렇게 사용해 본 결과

음... 결국

함수와 동일하게 생겼으니

값타입일까?

라고 생각할 수 있으나

 

클로저 타입

|

참조타입

 

값의 저장은 heap (주소 자체는 stack저장)

 

RC를 통한 메모리 관리를 하고 있습니다

 

 

클로저 외부 변수인 num에 대한 참조를 캡처하여

num에 대한 참조를 클로저가 유지하고 있고

클로저 내부에서 num의 값을 변경하면 실제로 num의 값이 변경됩니다

 

num=0을 재설정한 후에도 클로저는 여전히 동일한

num변수를 참조하고 있습니다

 

여기서 중요한건

num을  참조하고 수정하는 것이 가능하고

클로저 밖에서도 num을 수정하는것이 동일한 변수로 가능하나는 점을 볼 수 있습니다

 

 

 

응?? 지역변수인데 왜 값이 그대로 쌓이는거지??!!

 

이걸 좀 더 자세히 설명하자면

 

외부에서 capture를 가리키는 포인터를 제공한다고 생각하면 됩니다

 

sum은 현재 지역변수로 되어있기 때문에

일반 함수같은 경우는 초기화가 되는게 정상입니다

 

하지만 num이 지역변수더라도 클로저가 이 변수를 캡처해서 저장하기 때문에

calculateCaptrue 함수가 종료되었지만 sum의 변수는 클로저 내부에서 유지되어집니다

 

클로저 특성상 자신이 정의된 범위의 변수를 캡처하고 유지할 수가 있습니다

함수 내부에서 정의된 클로저인

square는 외부 범위의 변수 sum을 캡처하고 있습니다

 

 

고로 다시 한 번 말하지만

sum

바깥 함수인 calculateCapture 안에서 선언된 변수

 

return square

square 함수를 반환하여 클로저로 사용하고 있습니다

 

calculateCapture 함수를 호출하여 클로저 변수를 capture에 저장하게됩니다

 

이렇게 말해도 이해가 안간다면

 

클로저는 참조 타입이고

calculateCapture()을 변수에 저장하는순간 메모리주소가 생기면서

저 안에 있는 것들이 클래스처럼 heap에 저장이되어서 변하지 않는다라고 생각하면

편할 수도 있을것 같습니다

 

 

다음으로 클로저에서 매개변수에서 사용하는

키워드에 대해서 알아보고자 합니다

 

@escaping 키워드

함수의 실행이 종료되면 파라미터로 쓰이는 클로저도 제거가 됩니다

클로저를 제거하지 않고 함수에서 탈출을 시키는것으로 

함수 내부에서 사용한 클로저를 외부 변수에 저장할 수 있습니다

GCD(비동기에서 사용을 많이 하고 있음)

 

예를들어서

내가 메세지를 보내는 앱을 만든다고 했을 때

메시지를 보낼 때 네트워크를 사용해야하는데

네트워크 작업은 시간이 걸려서 나중에 결과가 올 때

클로저를 사용해서 메시지가 성공적으로 전송되었는지에 대해서 확인할 수가 있어

 

여기서 알아야할 점은

클로저가 함수가 끝난 후에도 사용할 수 있구나 라고 이해하면 됩니다

 

 

 

@autoclosure키워드

 

호출될 때 내부에 래핑된 표현식의 값을 반환하고 있습니다

그래서 중괄호 { }를 생략할 수 있습니다

 

그리고 또한 클로저가 호출될 때까지 코드 내부 실행이 되지 않기 때문에

자동클로저는 판단을 지연시킬 수 있습니다

 

판단 지연은 코드 판단 시기를 제어도 가능하기 때문에

사이드 이펙트나 계산이 오래 걸리는 코드에 유용합니다

 

 

 

var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
print(customersInLine.count)
// Prints "5"

let customerProvider = { customersInLine.remove(at: 0) }
print(customersInLine.count)
// Prints "5"

print("Now serving \(customerProvider())!")
// Prints "Now serving Chris!"
print(customersInLine.count)
// Prints "4"


// 그냥 함수를 사용할 때
// customersInLine is ["Alex", "Ewa", "Barry", "Daniella"]
func serve(customer customerProvider: () -> String) {
    print("Now serving \(customerProvider())!")
}

//{} 를 사용하는걸 볼 수 있다
serve(customer: { customersInLine.remove(at: 0) } )
// Prints "Now serving Alex!"

//클로저 @autoclosure 키워드를 사용했을 때
// customersInLine is ["Ewa", "Barry", "Daniella"]
func serve1(customer customerProvider: @autoclosure () -> String) {
    print("Now serving \(customerProvider())!")
}

//{} 를 생략된걸 볼 수 있다
serve1(customer: customersInLine.remove(at: 0))
// Prints "Now serving Ewa!"

 

 

그리고 이 두 개의 키워드를 가지고 활용도 가능합니다

// 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!"

 

 

customerProviders는 고객들의 대기열을 나타내고

대기중인것을 볼 수 있습니다

 

collectCustomerProviders

함수는 오토클로저와 탈출 클로저를 함께 사용하고 있습니다

이 함수는 고객제공자를 배열에 추가하는 역할을 하고 있습니다

여기서 자동으로 클로저가 생성되도록 하고 함수를 탈출하여도 실행할 수 있게 도와주고 있습니다

 

그래서 첫 번째 /두번째 고객을 제거하고 해당 고객의 클로저를 만들고 있습니다

 

이렇게 클로저에 대해서 대략적으로 정리해 보았습니다

 

 

❤️혹시나 잘못된 부분이 있다면 댓글로 알려주면 감사하겠습니다❤️

 

 

 

 

 

 

 

✏️참고

앨런스위프트 문법 자료(강의)⭐️⭐️⭐️ -추천

꼼꼼한 재은씨의 스위프트 프로그래밍

Swift 공식문서

구글번역기

 
반응형