[Swift] Closure
안녕하세요 우디입니다 🤪
최근에 면접을 봤는데 많이 어렵네요...
한번씩 공부하고 사용한 개념들이지만 긴장해서 그런건지 제대로 공부하지 못한건지 스스로 아쉬운 대답이 많았습니다 ㅠㅠ
그래서 '아 이렇게 대답한게 맞나...?' 한 부분에 대해 다시한번 공부하고 정리해보는 시간을 가지려고 합니다.
야곰 아카데미에서 야곰이 종종 하는 말이 있습니다.
'설명하지 못하면 모르는거다'
이번에 공부해볼 내용은 클로져입니다.
Closures — The Swift Programming Language (Swift 5.5)
Closures Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages. Closures can capture and store referen
docs.swift.org
클로저란?
전달하여 사용할 수 있는 기능 블록입니다. 클로저는 정의된 컨텍스트에서 모든 상수 및 변수에 대한 참조를 캡쳐하고 저장할 수 있습니다.
클로저 표현식은 다음과 같습니다
{ (parameters) -> return type in
statements
}
어디서 많이 본 것 같죠? 함수 정의와 많이 비슷합니다
func methodName(parameters: ParametersType) -> ReturnType {
statements
}
클로저의 특징 중 하나인 유형 추론은 매개변수의 유형과 반환하는 값의 유형을 유추할 수 있습니다.
즉 매개변수 이름 주위의 괄호도 생략할 수 있습니다.
let numbers = [1, 7, 4, 6, 8, 2, 3, 5]
// 원래대로라면 클로저에 (Int, Int) -> Bool 이렇게 명시된다
let sortedNumbers = numbers.sorted(by: { s1, s2 in return s1 > s2 })
Capturing Values
클로저의 설명에 나와있던 컨텍스트에 대한 참조를 캡쳐하는 기능입니다.
재밌는게 클로저는 더 이상 존재하지 않는 경우에도 클로저 본문 내에서 캡쳐하고 있는 상수 및 변수의 값을 참조하고 수정할 수 있는데요.
예시를 한번 보겠습니다
func makeIncrementer(forIncrement amount: Int) -> () -> Int {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
함수 makeIncrementer는 매개변수로 Int 타입을 받고 반환 타입으로 () -> Int 클로저를 반환합니다.
그리고 함수 내부에서는 () -> Int 함수 타입의 incrementer 메서드가 있습니다.
그리고 incrementer 함수는 클로저로서 외부의 값인 runingTotal를 캡쳐해 사용하고 있습니다.
원래 함수의 지역변수는 함수 호출이 종료되면 메모리에서 사라지는데 클로저의 값 캡쳐로 인해 지역변수 runningTotal은 계속 참조되고 있습니다.
let incrementByTen = makeIncrementer(forIncrement: 10)
incrementByTen()
// returns a value of 10
incrementByTen()
// returns a value of 20
incrementByTen()
// returns a value of 30
어...? 분명히 사라져야할게 사라지지 않는다...?
순환참조 낌새가 보인다...?
클로저는 참조 유형
공식문서를 따르면 incrementByTen에서 반환하는 유형은 Int로 분명한 값 타입입니다. 하짐나 클로저에서 캡쳐하는 변수는 값 타입이어도 참조합니다. 그 이유는 함수와 클로저가 참조 유형이기 때문입니다.
따라서 클래스 인스턴스의 속성에 클로저를 할당하고 클로저가 인스턴스 또는 해당 멤버를 참조하여 해당 인스턴스를 캡처하는 경우에 클로저와 인스턴스 사이에 순환참조를 생성할 수 있습니다.
Swift의 ARC문서를 참고하면 순환참조를 깨기 위해 약한 참조를 권유하고 있습니다.
제가 ARC 글에 써놓은 클로저와 순환참조 글을 참고해보면 좋을 것 같습니다
(나는 왜 공부해놓고 대답을 못했는가😂)