본문 바로가기

Swift

[Swift] Closure

안녕하세요 우디입니다 🤪

 

최근에 면접을 봤는데 많이 어렵네요...

한번씩 공부하고 사용한 개념들이지만 긴장해서 그런건지 제대로 공부하지 못한건지 스스로 아쉬운 대답이 많았습니다 ㅠㅠ

 

그래서 '아 이렇게 대답한게 맞나...?' 한 부분에 대해 다시한번 공부하고 정리해보는 시간을 가지려고 합니다.

 

야곰 아카데미에서 야곰이 종종 하는 말이 있습니다.

'설명하지 못하면 모르는거다'

 

 

이번에 공부해볼 내용은 클로져입니다.

 

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 글에 써놓은 클로저와 순환참조 글을 참고해보면 좋을 것 같습니다

(나는 왜 공부해놓고 대답을 못했는가😂)