Notice
Recent Posts
Recent Comments
Link
«   2024/05   »
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 31
Archives
Today
Total
관리 메뉴

Stacking Fire

Swift 13. 클로저 본문

개발/Objective-C -> Swift

Swift 13. 클로저

라우비 2017. 7. 12. 18:27

Objective-C의 블록 같은 녀석입니다....만 좀더 빈번히, 또 쉽게 사용됩니다. 함수형 언어에서 함수를 담당하고 있는 클로저!!

처음에 그 표현법 때문에 좀 당황을 먹었는데요, 스위프트를 겪으면서 쓰다 보니 점점 괜찮아져서, 이제는 클로저로 처리 가능한 것은 왠만하면 클로저로 처리하게 됐네요.

표현법

  • 통상적으로는 아래와 같음.

    {(parameters) -> ReturnType
        implementation
        return returnValue //있을 경우만!
    }
  • 예전의 코드. 생략의 HELL

    //아래 names 어레이를 역순으로 정렬하는 코드들.
    let names:[String] = ["wizplan", "eric", "yagom", "jenny"]
    
    //풀버전. first와 second를 가지고 어느 게 first가 클 때 true를 반환하는 클로저를 전달.
    let reversedFull: [String] = names.sorted(by: { (first: String, second: String) -> Bool in
        return first > second
    })
    
    //후행 클로저로 변환. sorted 함수가 받는 인자 이름 'by'와 함께 소괄호 생략, 각 인자 타입도 생략. 자동완성 기능이 추천하는 클로저 형태 (2017년 5월).
    let reversedAuto: [String] = names.sorted { (first, second) -> Bool in
        return first > second
    }
    
    //어차피 sorted가 받는 형태도 정해져 있고, 전달할 때도 Bool 전달하니까, 반환값 명시와 구분을 위한 괄호 생략
    let reversed: [String] = names.sorted { first, second in
        return first > second
    }
    
    //어차피 의미도 없는 first와 second 생략. 그냥 $0이랑 $1으로 대체하고 그냥 리턴
    let rvsd: [String] = names.sorted {
        return $0 > $1
    }
    
    //한 줄만 있으면 자동으로 리턴값 취급하므로 리턴도 생략..
    let rv: [String] = names.sorted {
        $0 > $1
    }
    
    // '>' 연산자도 함수고 어차피 두개 받아 Bool 리턴하는 거 똑같으니까( (_:,_:) -> Bool ) 그냥 >이것만 전달. 전달할 함수만 준비되어 있다면 자동완성으로 가능. (2017년 5월)
    let r: [String] = names.sorted(by: >)

캡처! Close!

  • 중첩 함수의 경우 상위 함수가 가진 아이를 capture해서 들고 있을 수 있다.
  • 상위 함수가 가져온 전달인자는 물론 함수 내부의 지역변수도 들고 있는다.
  • 일반적인 생명주기와 다른 행동이 일어나는 부분을 주목하자!

    //캡처하는 광경
    func makeIncrementor(for amount: Int) -> (()->Int) {
        var totalVariableForCapturing: Int = 0
        func resultClosure() -> Int {
            totalVariableForCapturing += amount
            return totalVariableForCapturing
        }
        return resultClosure
    }
    
    let incrementor2: Incrementor = makeIncrementor(for: 2)
    print(incrementor2()) // 2
    print(incrementor2()) // 4
    print(incrementor2()) // 6
    print(incrementor2()) // 8

탈출!!

  • completion 형태 등 함수 외부로 매개변수로 받은 클로저를 넘겨야 할 때가 있는데, 그럴 때 명시해주어야 함.
  • 키워드 @escaping을 사용한다. 반환하거나 다른 데로 보낼 때 쓴다.
  • 어디보자...

    //전달받은 클로저가 외부로 나갈 수 있기 때문에 @escaping 키워드 필요.
    func checkEvenIncrementor(_ array: [Incrementor], _ incrementor: @escaping Incrementor) -> [Incrementor] {
    
        let firstInt: Int = incrementor()
        let secondInt: Int = incrementor()
        if (secondInt - firstInt) % 2 == 0 {
            var resultArray = array
            resultArray.append(incrementor)
            return resultArray
        } else {
            return array
        }
    }
    
    func someOtherFunction(closure:@escaping Incrementor) {
        checkEvenIncrementor(evenIncrementorArray, closure)
    }
    
    var evenIncrementorArray: [Incrementor] = []
    
    for i in 1...30 {
        let incrementor: Incrementor = makeIncrementor(for: i)
        evenIncrementorArray = checkEvenIncrementor(evenIncrementorArray, incrementor)
    }
    
    for incrementor in evenIncrementorArray {
        print("\(incrementor()/3)")
    }

참조 타입이다!

  • class 말고도 있었다니...
  • 포인터를 넘겨줌. Block도 그랬던가...

자동 클로저

  • 구현할 일은 많지 않을 것..
  • 암튼, 보자.

    var customersInLine = ["Chris", "Alex", "Ewa", "Barry", "Daniella"]
    let customerClosure: () -> String = {customersInLine.remove(at: 0)}
    func serve(customer customerProvider: @autoclosure () -> String) {
        print(customersInLine, "실행함수안, 실행전")
        print("Now serving \(customerProvider())!")
        print(customersInLine, "실행함수안, 실행후")
    }
    func serveByStr(customer customerProvider: String) {
        print(customersInLine, "스트링버전실행함수안, 실행전")
        print("Now serving \(customerProvider)!")
        print(customersInLine, "스트링버전실행함수안, 실행후")
    }
    
    print(customersInLine, "실행전")
    serve(customer: customersInLine.remove(at: 0))
    print("\n\n스트링버전")
    print(customersInLine, "실행전")
    serveByStr(customer: customersInLine.remove(at: 0))
  • 결과창

    클로저버전
    ["Chris", "Alex", "Ewa", "Barry", "Daniella"] 실행전
    ["Chris", "Alex", "Ewa", "Barry", "Daniella"] 실행함수안, 실행전
    Now serving Chris!
    ["Alex", "Ewa", "Barry", "Daniella"] 실행함수안, 실행후
    
    스트링버전
    ["Alex", "Ewa", "Barry", "Daniella"] 실행전
    ["Ewa", "Barry", "Daniella"] 스트링버전실행함수안, 실행전
    Now serving Alex!
    ["Ewa", "Barry", "Daniella"] 스트링버전실행함수안, 실행후
    Program ended with exit code: 0


'개발 > Objective-C -> Swift' 카테고리의 다른 글

Swift 15. 맵, 필터, 그리고 리듀스  (0) 2017.08.12
Swift 14. 옵셔널 체이닝, 그리고 빠른 종료  (0) 2017.08.10
Swift 12. 접근제어  (0) 2017.07.10
Swift 11. 이니셜라이저  (0) 2017.07.03
Swift 10. 메서드  (0) 2017.07.01
Comments