본문 바로가기

Swift

09 Swift Closure

반응형


여기 저기 널려있는 글들을 다시 요약하였다.

  • 클로저
    • 사용자의 코드 안에서 전달되거나 사용할 수 있는 기능을 포함한 독립적인 블록
    • 자신이 정의된 컨텍스트 (context) 로부터 임의의 상수 및 변수의 참조(reference) 를 획득 (capture)하고 저장할 수 있습니다. (아래 값 획득하기 참조)
      • 클로저의 경우 클로저 바로 밖의 scope 의 상수 및 변수에 접근할 수 있다는 이야기입니다)
      • 이렇게 상수 및 변수를 제약하는 특징때문에 클로저라는 이름이 되었습니다.
    • Swift는 획득 과정의 메모리 관리를 모두 제어해줍니다
    • 클로저는 함수와 그 주변 환경에서 함수를 계산하는데 꼭 필요한 정보를 묶어서 저장해 둔 덩어리
  • javascript 의 클로저,  C# 의 람다
  • 클로징
    • 문맥에서 참조를 잡아서 저장하는 과정을 클로징 이라고 한다. 상수와 변수에 대해 그렇게 닫기 때문에 클로저라고 부른다.
  • 클로저의 3가지 형태
    • 전역 함수 : 이름은 있지만 아무 값도 획득하지 않는 클로저 ? - 그냥 함수다.
    • 중첩함수 : 이름이 있고, 내부의 함수의 값을 획득하는 클로저 ?  - 주변의 상수를 획득할 수 있는 구조
    • 클로저 표현식 : 자신을 둘러싼 컨텍스트에서 값을 획득할 수 있는 가벼운 문법 ? - 람다 코드 블럭

  • 전역 함수는 왜 클로징 하지 않나?
    • 결국 변수가 모든 곳에서 참조가 가능하기 때문


더 공부하기
MV - V - VM (MVVVM) + Command 패턴  C# WPF 에서 널리 쓰여지는… 디자인 패턴
     비지니스 로직, 프리젠테이션로직 UI 와 명확하게 분리
모델뷰 - 뷰 - 뷰모델 


클로저 표현식 (Closure expressions) inline closures

중첩함수는 더 큰 함수의 일부로서 동작 -> 함수내의 반복되는 코드들을 함수화
완전한 선언이나 이름이 없는 함수 구조 -> 다른 함수들을 하나 또는 그 이상의 인자로 받는 함수를 만들 때


EX) 정렬함수의 진화 (sorted)




let names = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]





함수 표현식



func backwards(s1: String, s2: String) -> Bool {
    return s1 > s2
}
var reversed = sorted(names, backwards)
// reversed i s equal to ["Ewa", "Daniell a", "Chris", "Barry", "Alex"]




클로저 표현식
  • 중괄호 안에 쓰여져야 한다.



{ ( parameters ) -> return type in 
    statements
}







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





컨텍스트로부터 타입 유추하기

(String, String) -> Bool  인라인 클로저 표현 형태로 클로저를 함수에 전달할 경우 인자와 반환 값의 타입을 유추할 수 있다. (생략)



reversed = sort(names, { s1, s2 in return s1 > s2 } )





단일 표현식 클로저로부터의 암시적 반환 

s1>s2 의 결과값음 Bool 형태이기 때문에 Bool 과 return 을 생략할 수 있다.



reversed = sort(names, { s1, s2 in s1 > s2 } )




단축 인자 이름들 
Swift는 자동으로 단축 인자 이름을 인라인 클로저에 제공하며, 클로저의 인자들은 $0, $1, $2 등등의 방식으로 참조할 수 있다.
이러한 단축 인자 이름들을 클로저 표현식에서 사용할 경우, 인자 리스트를 클로저의 정의에서 생략할 수 있다. 또한 in 키워드도 생략 할 수 있다




reversed = sort(names, { $0 > $1 } )




연산자 함수들 
String에 특화된 크기 비교 연산자 > 를 두 String 인자의 비교로 유추함




reversed = sort(names, > )







후행 클로저 (Trailing Closures)

후행 클로저는 함수 표현식으로 함수 괄호 밖에서 작성되어 지원함
긴 클로저 표현식을 함수의 마지막 인자로 함수를 전달할 필요가 있다면 후행 클러저로 유용하게 대신 사용할 수 있다.




func someFunctionThatTakesAClosure(closure: () -> ()) {
    // function body goes here
}
// here's how you call this function without using a trailing closure:

someFunctionThatTakesAClosure({
    // closure's body goes here
})

// here's how you call this function with a trailing closure instead:

someFunctionThatTakesAClosure() {
    // trailing closure's body goes here
}



후행 클로저는 클로저가 충분히 길어서 줄 안이나 한 줄 정도로 기술할 수 없는 경우에 아주 유용합니다. -> 클로저 부분을 맨 뒤로 뺌으로 써 가독성을 높인다.



let digitNames = [
    0: "Zero", 1: "One", 2: "Two",   3: "Three", 4: "Four",
    5: "Five", 6: "Six", 7: "Seven", 8: "Eight", 9: "Nine"
]
let numbers = [16, 58, 510]





let strings = numbers.map {
    (var number) -> String in
    var output = ""
    while number > 0 {
        output = digitNames[number % 10]! + output
        number /= 10
    }
    return output
}
// strings is inferred to be of type String[]
// its value is ["OneSix", "FiveEight", "FiveOneZero"]



16 =  “One” + “Six"
58 = “Five” + “Eight"
510 = “Five” + “One”  +”Zero"



값 획득하기 (Capturing Values)

  • 클로저는 자신이 정의된 주변 컨텍스트로부터 상수 및 변수의 값을 획득할 수 있습니다.
  • 클로저는 이러한 상수와 변수들을 원래 상수와 변수들이 정의된 범위 (scope) 가 더이상 존재하지 않는 경우에조차도 값을 참조하거나 수정할 수 있습니다.
  • Swift에서 클로저의 가장 간단한 형태는 다른 함수의 본문 안에 작성된 중첩 함수입니다.
  • 중첩 함수는 바깥 함수의 모든 인자를 획득할 수 있으며, 또한 바깥 함수 내에서 정의된 모든 상수 및 변수를 획득할 수 있습니다.


아래는 Incrementor라는 중첩 함수를 포함한 makeIncrementor 예입니다.
중첩된 incrementor 함수는 runningTotal 및 amount 의 두 값을 자신을 둘러싼 컨텍스트로부터 획득합니다.
이 두 값을 획득한 후, incrementor는 호출될 때 마다 runningTotal 을 mount 만큼 증가시키는 클로저로써makeIncrementor 로부터 반환됩니다.



func makeIncrementor(forIncrement amount: Int) -> () -> Int {
    var runningTotal = 0
    func incrementor() -> Int {
        runningTotal += amount
        return runningTotal
    }
    return incrementor
}




let incrementByTen = makeIncrementor(forIncrement: 10)
let incrementBySeven = makeIncrementor(forIncrement: 7)
incrementByTen() // 10 반환
incrementBySeven() // 7 반환
incrementByTen() // 20 반환
incrementByTen() // 30 반환


참조 타입인 클로저 (Closure Are Reference Types)

위에 예제에서 incrementBySeven과 incrementByTen은 상수지만 클로저는 여전히 runningTotal 변수를 증가시키도록 참조함.
이것은 함수와 클로저는 참조 타입이기 때문임.
함수나 클로저를 상수에 할당하는 때는 그 상수에 함수나 클로저를 가르키는 참조를 할당하는 것임.
위의 예제에서 incrementByTen 상수는 클로저를 참조하며, (클로저 내용은 아님.)

이 의미는 다른 두개의 상수나 변수에 동일한 클로저를 할당하면 같은 클로저를 참조한다는 의미.