Swift) closure와 @escaping 이해하기
안녕하세요? :> 오랜만입니다
오랜만인 이유는 5, 6월 현업이 매우매우x100 바쁘기도 했고,
그에 따른 번아웃이 온 건지,, 한 달정도 공부에 대한 반항을 좀 해서,, ㅎㅎ;;
쨌든 다시 정신 차리고 돌아왔습니당
우리 블로그,, 정상영업 합니다,,
오늘 포스팅은 오랜만에 Swift에 관련된 거예요!
closure와 escaping은 예전 closure 포스팅에서 다룬 적이 있지만,
이 파라미터로 전해지는 closure가 optional로 감싸지는 순간 얘기가 달라지거등여
이 부분에 대해 공부를 해볼 예정입니당!!
먼저, closure와 @escaping이 무엇인지,
@escaping을 왜 쓰는지에 대해서는 적어도 사전 지식이 있어야 해요!
모르신다면 이 포스팅과 이 포스팅을 꼭 보고 오시길 바라곘습니당
그럼 안다는 가정 하에 공부하러 가봅시당 :D
모든 포스팅은 편의 말투로 합니다~!!
1. 클로저에서 @escaping은 언제 붙일까?
사실 위에서 포스팅 읽고 오라고 했지만 ㅎ^ㅎ
안 읽고 온 누군가를 위해 덧붙이자면,
다음과 같은 코드가 있음
func sodeul(completion: () -> ()) {
completion()
}
|
이때 위 코드에서 completion이라는 매개변수의 타입은
매개변수가 없고, 리턴 타입도 없는 () -> () 이라는 함수 타입임
어떻게 파라미터로 클로저를 받을 수 있는 걸까?
Swift에서 클로저는는 1급 객체니까!!ㅎㅎㅎ
변수나 상수에 대입 가능! 파라미터 인자로 넘겨주기도 가능!
실제로 사용할 땐 이렇게 하잖음?
sodeul {
print("hungry😭") // hungry😭
}
|
이렇게! 매개변수로 함수를 넘겨줄 수 있고,
실제로 그 매개변수로 넘겨준 함수가
sodeul이란 함수 내부에서 completion이란 매개상수로 저장되어 사용 되잖음!?
(위 문법이 어렵다면 trailing closure를 공부해주세영)
자, 근데 다시 함수 선언부로 돌아가서
내가 매개변수로 받은 completion에 저장된 클로저를 3초 후에 실행시키고 싶음
따라서 다음과 같이 코드를 짜주면
func sodeul(completion: () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
completion()
}
}
|
이렇게 짤 경우,
이런 에러가 뜸 🐛
왜 이런 에러가 뜨냐!?!?
우리가 일반적으로 위처럼 아무런 키워드 없이 파라미터로 받는 클로저는
모두 non-escaping closure임!!
이름 그대로 탈출이 블가능한 클로저임
함수의 "흐름"을 탈출하지 않는 클로저란 뜻으로,
한 마디로 함수가 종료되고 나서 클로저가 실행될 수 없다는 말임
함수가 종료될 때 클로저의 사용도 같이 종료되어서 같이 ㅃㅃ 해야하는 것임
근데 위 함수 같은 경우는 asnyc로 3초뒤에 클로저를 실행되게끔 해버렸음
그러면 어떻게 되느냐
당장 completion은 에러가 뜨니, 위처럼 print 함수로 예를 보자면,
함수 내부에서 3초 뒤에 실행되는 Async Task(👍)를 줘버렸기 떄문에
이론상 함수의 생애주기가 완전 끝나버렸지만, 그 이후에 해당 작업이 실행되게 됨
이것이 바로 함수의 흐름을 탈출한 상황인데,
3초 뒤에 실행되는 Async Task(👍)를 completion으로 다시 바꾼다 생각해보셈
completion은 함수가 끝나기 전에 실행이 모두 종료되어야 하는데,
함수의 흐름을 벗어나 호출되어야 하기 때문에 이는 non-escaping parameter가 아니야!
하고 에러가 생긴 것임 오키!?
따라서 non-escaping closure의 경우
함수 내부에서 직접 실행하기 위해서만 사용한다
따라서 파라미터로 받은 클로저는 변수나 상수에 대입할 수 없고,
중첩 함수 내부에서 클로저를 사용할 경우, 중첩함수를 리턴할 수 없다
함수의 실행 흐름을 탈출하지 않아, 함수가 종료되기 전에 무조건 실행 되어야 한다
이런 특징들을 가지고 있음
그럼 파라미터로 받은 closure는 비동기로 사용못해!?? 함수 흐름 탈출 못해!?!?
할때 사용하는 키워드가 바로
@escaping
임!! :)
func sodeul(completion: @escaping () -> ()) {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
completion()
}
}
|
이런 식으로 함수의 타입 선언하기 전에
@escaping이란 키워드를 붙여주면,
이 클로저는 함수의 실행 흐름에 상관 없이 실행되는 클로저다!라고 알려주는 것임
따라서 실행해보면 에러 없이 completion이 잘 실행됨!
🍊가 먼저 실행되고, 3초 뒤에 😭가 실행 될 것임!
이것이 바로 @escaping 이랍니다 :)
2. 클로저를 Optional Type으로 선언하면 말이죠
근데 있잖음 파라미터로 받을 클로저가 있을 수도, 없을 수도 있잖음?
따라서 다음과 같이 클로저를
func sodeul(completion: @escaping (() -> ())?) {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
completion?()
}
}
|
위처럼 옵셔널 타입으로 감싸서 선언해주면 말이셈
클로저가 이미 escaping이니 @escaping을 삭제하라 함
따라서 fix 버튼을 누르면 말이셈
func sodeul(completion: (() -> ())?) {
DispatchQueue.main.asyncAfter(deadline: .now() + 3) {
completion?()
}
}
|
???
분명 똑같이 함수의 흐름을 탈출해서 3초뒤에 실행되는 클로저임에도 불구하고
@escaping이 사라짐!! 왜 이난리 ㅎㅎ;
따라서 이 난리의 이유에 대해 알아보기 위해 쓴 글임!
3. 파라미터 타입이 Optional Closure일 경우, 더이상 Closure 파라미터 타입이 아닌 Optional 파라미터 타입이다
이게 무슨 말인지 알아보기 위해,
먼저 Optional Closure가 아닐 경우의 파라미터 completion의 타입을 보겠음!!!
이때 completion이라는 매개상수의 파라미터 타입은
() -> ()로 함수 타입 즉, "클로저 타입"임
근데 옵셔널을 붙이면 어떻게 될까?
optional을 붙이는 순간 completion의 타입은
(() -> ())?가 되며, 이는 더이상 클로저 타입이 아님
() -> ()라는 함수 타입을 제네릭 함수의 Type Parameter로 받은 "옵셔널 타입"임!!
이해가 안 갈 수도 있을 것 같은데,
가장 대표적인 제네릭인 Array를 생각해보셈!!!
Int와 Array<Int>는 서로 완전히 다른 타입이잖음??
그와 마찬가지로 함수 타입인 ()->()와,
이 함수 타입을 옵셔널로 감싼 (() -> ())?는 서로 완전히 다른 타입임!!!!
예전에 옵셔널을 공부할 때 Optinal을 선언하는 순간,
Optional이라는 "포장지"가 씌워지는 것이기 때문에
더이상 String 타입이 아닌, Optional<String> 타입이라고 말 했음!!
따라서 사용할 때도 Optional Binding을 통해 포장지를 벗겨줘야 했잖음??
이런 관점에서 보면 이제 이해가 감?
함수 타입을 제네릭 타입인 옵셔널로 감싸버렸기 때문에,
더이상 함수(클로저) 타입이 아니여서
기본적으로 파라미터로 받는 "클로저"는 함수 흐름을 탈출하지 못한다
위 조건에서 "클로저"조건이 벗어나버린 것임!!!!
근데 이렇게 함수 파라미터의 클로저가 Optional Type인 경우엔
자동으로 escaping으로 동작하나봄!! 따라서 @escaping을 지우라는 듯..?
(제가 잘못 이해한거라면 말씀.. 주세.. 여..)
4. 자동으로 escaping으로 동작하기 때문에, @escaping을 붙이지 않아도 정상 작동합니다
제목이 즉 결론임!!
위에서 completion이란 클로저는 함수의 흐름을 탈출하여 실행되지만,
Optional Type으로 된 Argument이기 때문에
@escaping이란 키워드를 붙여주지 않아도, 자동으로 escaping으로 동작 된답니당 :)
.
.
.
너무 오랜만에 글을 써서 마음에 들진 않지만.. :)
앞으로는 다시 회사의 등대가 되어 열심히 공부하며 글을 써보도록 하겠습니다!
아마 디자인 패턴에 관한 글도 많이 올라올 것 같은데 ㅎㅎㅎㅎ
쨌든 만약 잘못된 내용이나 피드백 있으면 댓글 주세요!!