티스토리 뷰

반응형

안녕하세요 Pingu입니다.

 

Swift 문법 중 Closure(클로저)에 대해서 다시 공부를 하는데 AutoClosure, EscapingClosure이 잘 이해가 가지 않아서 정리 겸 공부를 한 번 하기 위해 글을 쓰려고 합니다. 클로저에 대한 내용은 여기서 한 번 정리를 했는데도, AutoClosure, EscapingClosure을 다시 보니 잘 기억이 나지 않네요.. 역시 어려운 개념은 반복학습을 해야 되는 것 같아요. 만약 클로저를 처음 보시는 분이라면 여기를 먼저 보시는 것을 추천드립니다!

 

어쨌든 이번 글에서는 AutoClosure, EscapingClosure에 대해 공부해보겠습니다.

Closure(클로저)

우선 공식문서에서 클로저는 코드에서 함수적인 것을 독립적으로 사용할 수 있는 코드라고 정의합니다. 저는 클로저가 함수를 내포하고 있는 개념이라고 이해했는데, 그 이유는 전역 함수, Nested function 모두 클로저라고 문서에 작성되어있었기 때문입니다. 그중 이름이 없는 클로저를 closure expression이라고 표현하고 있었고 따라서 이름이 있는 클로저를 "함수"라고 부른다고 이해했습니다.

 

어쨌든 이글에서는 클로저에 대한 부분은 간단하게 사용법만 보고 넘어가겠습니다.

var names = ["Roby", "Pinga", "Pingu"]

func sortByAlphabetOrder(_ s1: String, _ s2: String) -> Bool {
    return s1 < s2
}

names = names.sorted(by: sortByAlphabetOrder)

print(names)

위의 코드는 string 배열을 알파벳 순으로 정렬하는 코드입니다. sorted라는 메서드에 제가 정의한 sortByAlphabetOrder 이라는 함수를 매개변수로 준 것을 볼 수 있습니다. 이렇게 하면 위와 같이 string 배열 속 값들이 알파벳 순으로 정렬되는데, 이를 함수를 따로 정의하는 것이 아닌 클로저 표현식으로만으로도 할 수 있습니다.

var names = ["Roby", "Pinga", "Pingu"]

names = names.sorted { (s1: String, s2: String) -> Bool in
    return s1 < s2
}

print(names)

위와 같이 코드를 작성해도 결과는 동일하게 나옵니다. 이렇게 사용하는 클로저를 Trailing Closure(후행 클로저)라고 공식문서에는 정의되어 있습니다. 물론 후행 클로저를 사용할 때 간단하게 축약하는 방법들도 있지만 여기서는 다루지 않으려고 합니다. 클로저는 어떠한 함수 안에서 함수처럼 동작하는 것을 사용하고 싶을 때 따로 정의하지 않고 사용할 수 있는 방법!이라고 생각하시면 됩니다.

Escaping Closure

그럼 일반적인 클로저에 대해 간단히 알아봤으니 이젠 Escaping closure에 대해 알아보겠습니다. 얘는 이름을 직역하면 탈출 클로저라고 하는데 저도 처음엔 이게 뭔말인지 이해가 안 갔는데, 계속 보다 보니 이해가 갔습니다.

 

우선 중첩 함수를 생각해보면, 함수 안에 정의된 함수는 함수 내의 지역 변수에만 접근이 가능했습니다. 근데 만약 함수 밖의 리소스들에 접근하고 싶다?고 할 때 사용할 수 있는 것이 바로 escaping closure입니다! Escaping Closure를 사용하려면 클로저 앞에 @escaping이라는 키워드를 써주시면 됩니다. 그럼 바로 한 번 사용해볼게요!

var 전역변수 = [() -> Void]()
func withEscapingClosure(completion: @escaping () -> Void) {
    completion()
    전역변수.append(completion)
}

// 매개변수로 받는 클로저가 escaping 클로저가 아니므로 전역변수에 접근할 수 없습니다
func noEscapingClosure(completion: () -> Void) {
    completion()
    전역변수.append(completion)
}

위와 같이 이해가 쉽도록 함수 외부의 변수를 전역변수라고 만들었습니다. 그리고 2개의 함수를 정의해서 하나는 escaping 클로저로, 하나는 그냥 일반 클로저로 만들었습니다. noEscapingClosure() 함수의 클로저는 escaping closure가 아니기 때문에 전역 변수에 접근할 수 없습니다. 따라서 아래와 같이 오류가 발생합니다.

즉 함수 안에서 정의된 클로저가 외부 변수들에 대한 접근을 허용할 때 escaping closure을 사용하면 됩니다.

 

또한 escaping closure를 사용할 때 주의할 점 중 하나는 참조 타입에서 self를 사용하는 경우 입니다. 쉽게 이해할 수 있는 내용인데 공식문서에는 조금 어렵게 풀어져있더라고요. 그러니까 escaping 클로저는 외부의 변수들에도 접근이 가능했었죠? 그러니까 현재 접근하는 변수가 자신의 스코프 안에 있는 변수인지 외부에 존재하는 변수인지를 명확하게 해야 하므로 self를 사용해야 한다는 것입니다.

그러니까 위와 같이 x라는 이름의 변수가 각자 다른 접근 권한을 가진 채로 2개 존재할 때 위와 같이 escaping closure를 사용할 때는 접근할 수 있는 경우의 수가 많기 때문에 어디에 접근하는 것인지 명확하게 해줘야 한다는 것입니다. 

위와 같이 Class와 같은 참조 타입에서는 캡처를 사용하여 이를 해결할 수도 있고 명시적으로 self를 사용하여 이를 해결할 수 있습니다. 그럼 위의 코드에서 escaping closure에서 외부의 x에 접근하려면 어떻게 해야 할까요? 외부에 존재하는 x라는 변수가 속한 스코프의 이름으로 접근하면 됩니다.

 

그렇다면 Class와 같은 참조 타입이 아닌 Struct, enum과 같은 값 타입에서는 어떨까요? Escaping closure에는 mutating reference의 캡처를 허용하지 않기 때문에 Struct, enum 같은 값 타입에서는 escaping closure로 self의 사용이 불가능합니다.

 

 

Auto Closure

그럼 이제 마지막으로 Auto Closure에 대해 알아보겠습니다. 얘는 자동 클로저? 정도로 해석할 수 있는데 뭘 자동으로 해주느냐가 중요합니다. Swift에는 함수 타입이라는 것이 있습니다. 즉 어떤 상수나 변수에 함수를 저장해둘 수 있다는 말인데요 이를 간단하게 해 보면 아래와 같습니다.

함수를 하나 선언하고 변수에 넣으면 위와 같이 변수의 타입이 () -> String, 즉 함수 타입이 됩니다. 얘를 매개변수로 쓸 수 도 있죠.

func returnPinguFunction() -> String {
    return "Pingu"
}
var myClosure = returnPinguFunction

func exampleFunction(_ closure: () -> String) {
    print(closure())
}
exampleFunction(myClosure)

이렇게 하면 함수 타입의 변수를 사용할 수 있는데요, 그럼 여기서는 String 값을 반환하는 함수를 매개변수로 받는 것이죠. 근데 여기서 Auto closure를 사용하게 되면 함수 타입의 반환 타입 즉 여기서는 String 타입이 매개변수의 타입이 됩니다. Auto Closure를 사용하는 방법은 매개변수의 타입 앞에 @autoclosure를 써주시면 됩니다.

즉 위와 같이 string 타입의 값을 매개변수로 사용하게 되는 것입니다.

물론 함수 타입을 실행한 값을 매개변수로 줄 수도 있습니다.

클로저의 반환 타입으로 매개변수의 타입을 자동으로 변환하여 사용하게 하므로 Auto Closure일까요?

 

이렇게 Escaping Closure, Auto Closure에 대해 알아봤습니다. 글을 쓰다 보니 조금 이상해진 느낌도 있지만 도움이 되셨길 바랍니다ㅠㅠ

감사합니다.

반응형
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
1 2
3 4 5 6 7 8 9
10 11 12 13 14 15 16
17 18 19 20 21 22 23
24 25 26 27 28 29 30
글 보관함