상세 컨텐츠

본문 제목

Nested Function (feat. Closure in Software Architecture)

Swift&SwiftUI

by (방울)도마토 2024. 9. 8. 17:44

본문

중첩 함수(Nested Function) 

- 내부 함수(Inner Function): 함수 내에 작성된 함수 

- 외부 함수(Outer Function): 내부 함수를 포함하는 바깥쪽 함수

 

// 외부 함수 
func outerFunction() -> Int {
    // 내부 함수
    func innerFunction(x: Int) -> Int {
        return x * x
    }
    
    return innerFunction(x: 5)
}

outerFunction()		// 25

 

1. 함수 내에 작성할 수 있는 내부 함수의 수에는 제한이 없음 

2. 외부 함수 내에 작성된 내부 함수에 또 다른 내부 함수를 작성할 수도 있음 

3. 내부 함수는 일반적으로 외부 함수를 거치지 않으면 접근할 수 없음 

    → 함수의 은닉성: 내부 함수는 외부의 코드로부터 차단됨

 

내부 함수의 생명주기

- 내부 함수는 외부 함수가 실행되는 순간 생성, 종료되는 순간 소멸 

→ 외부 함수는 프로그램이 실행될 때 생성되고 프로그램이 종료될 때 소멸하지만, 내부 함수는 외부 함수의 실행과 종료 사이에서 생겼다가 사라짐 

⇒ 외부 함수가 종료되면 내부 함수도 더는 존재하지 않음  

 

* 생각해봐야 할 점

1. 내부 함수 접근

- 아래의 예시를 통해 은닉성이 있는 내부 함수 inner를 외부 함수의 실행결과로 반환함으로써 내부 함수를 외부에서도 접근할 수 있게 됨..!

- 내부 함수인 inner는 외부 함수인 outer가 실행 종료되면 소멸되어야 함 

    - let function01 = outer(param: 3) 이 실행되면 inner가 실행되고 소멸되어야 함

- 그러나, inner 함수가 소멸되지 않고 function01에 할당된 채로 생명을 유지하다가 함수 호출 연산자 (30) 을 만나 실행되고 있음 

→ 외부 함수에서 내부 함수를 반환하게 되면 외부 함수가 종료되더라도 내부 함수의 생명이 유지됨!(참조 카운트가 존재하기 때문!)

func outer(param: Int) -> (Int) -> String {
    func inner(int: Int) -> String {
        return "\(int)를 반환!"
    }
    
    return inner
}

let function01 = outer(param: 3)  // outer()가 실행 -> function01에 inner() 대입 (일급 함수의 특성: 변수에 함수 할당 가능)
let function02 = function01(30)    // inner(30) 과 동일

 

 

 

2. 외부 함수의 지역 상수, 지역 변수가 내부 함수에 참조될 때

- 외부 함수 outer는 let function01 = outer(param: 3) 구문의 실행 종료와 함께 제거됨 (해당 함수를 참조하는 곳이 없기 때문)

- 만약 outer 함수 내부에 지역 변수가 정의되어 있다면 함수 종료시 함께 제거 

    → 지역 변수는 자신을 선언한 블록의 실행과 함께 생명주기를 가짐 

- value 상수는 let result = basic(param: 10)이 실행되고 종료될 때 함께 사라져야만 함 

    → result(10)이 실행되는 시점에 value 상수가 더는 존재하지 않으며, append 함수 내부의 블록에서는 결과적으로 존재하지 않는 상수를 참조하는 모습이 됨 → 오류..!

- 그러나, 전혀 문제 없이 동작하며 40 이란 정수도 반환함 

→ append 함수가 클로저(Closure)를 갖기 때문..!

func basic(param: Int) -> (Int) -> Int {
    let value = param + 20  // basic의 지역 상수
    
    func append(add: Int) -> Int {
        return value + add  // basic의 지역 상수 value를 참조하고 있음
    }
    
    return append(add:)
}

let result = basic(param: 10)   // result에 append(add:) 할당

result(10)  // value -> 10 + 20, append(10) -> 30 + 10 = 40

 

* 클로저: 내부 함수와 내부 함수에 영향을 미치는 주변환경(context)를 모두 포함한 객체

1) 내부 함수와 내부 함수가 만들어진 context로 이루어진 객체

2) 외부 함수 내에서 내부 함수를 반환하고, 내부 함수가 외부 함수의 지역 변수나 상수를 참조할 때 생성

 

* context: 내부 함수에서 참조하는 모든 외부 변수나 상수의 값, 내부 함수에서 참조하는 다른 객체까지 포함 

→ 클로저에서 저장하는 context는 변수, 객체의 값을 의미

→ 클로저가 만들어지기 위해서는 실제로 basic 함수가 호출되어야 함

 

closure가 만들어지기 위해서는 let result = basic(param: 10)이 실행되어야 함

 

// 상수 result에 저장되는 클로저의 형태
func append(add: Int) -> Int {
    return 30 + add	// 내부 함수를 둘러싼 context 객체가 값으로 바뀌어 저장
}

// 같은 정의를 갖는 함수가 서로 다른 환경을 저장하게 됨 
let result01 = basic(param: 5)
let result02 = basic(param: 15)

// result01에 할당된 클로저 
func append(add: Int) -> Int {
    return 25 + add	
}

// result02에 할당된 클로저 
func append(add: Int) -> Int {
    return 35 + add	
}

 

- 외부 함수에서 정의된 객체가 내부 함수에서도 참조되고 있고, 내부 함수가 반환되어 참조가 유지되고 있는 상태라면 클로저에 의해 내부 함수 주변의 지역변수나 상수도 함께 저장됨 

→ 정확히는 지역 변수의 값이 '저장' 되는 것

⇒ 값의 capture : context에 포함된 변수나 상수 타입이 기본 자료형이나 구조체 자료형일 때 발생 

관련글 더보기

댓글 영역