관리 메뉴

사과하는 제라스

[Swift 문법] Closure를 알아보자! 본문

제라스의 iOS 공부/Swift 문법

[Swift 문법] Closure를 알아보자!

Xerath(제라스) 2023. 8. 17. 13:14

목차

    728x90
    반응형

    일단 클로저는 내가 부스트캠프를 하면서 배웠던 것 중 가장 중요한 Swift 문법 중 하나라고 생각했다.

    특히, 이것을 전혀 모르고 있었던 것이 내가 다른 캠퍼들의 코드를 이해하는데에 자꾸 걸림돌이 되었었다.

    그래서 이번엔 클로저를 정리해보도록 하겠다.

     

    먼저 내가 이해한 클로저는 사실 메소드와 비슷한 개념이라고 보았다.

    일단, 클로저를 사용하는 예제들을 위주로 보자.

     

    import UIKit
    
    // String을 반환하는 클로저
    let myName : String = {
        // 여기서 return되는 값이 myName으로 들어가진다.
        return "제라스"
    }()
    
    print(myName)
    
    // String을 받아서 다른 이름으로 return 하는 클로저
    let playingName : (String) -> String = { (name: String) -> String in
        return "숭실대 \(name)"
    }
    
    myRealName("제라스")
    
    // 이렇게 하면 클로저를 바로 함수처럼 사용할 수 있다.
    let myRealNameLogic : (String) -> Void = { (name: String) in
        print("return하지 않은 클로저 with \(name)")
    }
    
    myRealNameLogic("제라스")

    이렇게 클로저를 이용해서 클로저변수를 만들 수도 있다.

     

    혹은 다음처럼 클로저를 매개변수로 사용이 가능하다.

    import UIKit
    
    // completion 이라는 클로저를 매개변수로 가지는 메소드(sayHi) 정의
    func sayHi(completion: () -> Void){
        print("sayHi() called")
        sleep(2) // 2초 잠깐 멈추기
        // completion 클로저 실행
        completion()
    }
    
    // 메소드 호출부 에서 이벤트 종료를 알 수 있다.
    sayHi(completion: {
        print("2초가 지났다. 1")
    })
    
    sayHi(){
        print("2초가 지났다. 2")
    }
    
    sayHi{
        print("2초가 지났다. 3")
    }
    
    
    // (String) -> Void
    //func completion(userInput: String){
    //
    //}
    
    
    // 매개변수로서 데이터를 반환하는 클로저
    func sayHiWithName(completion: (String) -> Void){
        print("sayHiWithName() called")
        sleep(2)
        // 클로저를 실행과 동시에 데이터를 반환
        completion("오늘도 빡코딩 하고 계신가요?!")
    }
    
    sayHiWithName(completion: { (comment: String) in
        print("2초 뒤에 그가 말했다! comment: ", comment)
    })
    
    sayHiWithName(completion: { comment in
        print("2초 뒤에 그가 말했다! comment: ", comment)
    })
    
    sayHiWithName{ comment in
        print("2초 뒤에 그가 말했다! comment: ", comment)
    }
    
    sayHiWithName{
        print("2초 뒤에 그가 말했다! comment: ", $0)
    }
    
    // (String, String) -> Void
    //func completion(first: String, second: String){
    //
    //}
    
    // 매개변수로서 데이터를 여러개 반환하는 클로저
    func sayHiWithFullName(completion: (String, String) -> Void){
        print("sayHiWithFullName() called")
        sleep(2)
        // 클로저를 실행과 동시에 데이터를 반환
        completion("빡코딩", "호롤롤로")
    }
    
    sayHiWithFullName { first, second in
        print("첫번째: \(first), 두번째: \(second)")
    }
    
    sayHiWithFullName { _, second in
        print("두번째: \(second)")
    }
    
    sayHiWithFullName{
        print("첫번째: \($0), 두번째: \($1)")
    }
    
    func sayHiOptional(completion: (() -> Void)? = nil){ //기본은 nil로 하겠다.
        print("sayHiOptional() called")
        sleep(2) // 2초 잠깐 멈추기
        // completion 클로저 실행
        completion?() // <-이렇게 해야 optional을 터뜨릴 수 있다.
    }
    
    sayHiOptional() //이러면 클로저를 안넣어주고서 실행할 수 있다. completion?()이 실행되지 않는다.
    
    sayHiOptional(completion: {
        print("2초가 지났다.!!")
    })
    
    
    // (Int) -> String
    //func transform(number: Int) -> String {
    //    return "숫자 : \(number)"
    //}
    
    var myNumbers : [Int] = [0, 1, 2, 3, 4, 5]
    
    //var transformedNumbers = myNumbers.map { (aNumber: Int) -> String in
    //    return "숫자: \(aNumber)"
    //}
    
    //var transformedNumbers = myNumbers.map { aNumber in
    //    return "숫자: \(aNumber)"
    //}
    
    var transformedNumbers = myNumbers.map {
        return "숫자: \($0)"
    }

     

     

    여기서 이런 식으로 클로저가 매개변수로 쓰이는 함수를 호출 시,

    매개변수에 클로저를 할당하는 방법은 다양하게 가능하다. (나는 뭔가 마지막 방법이 편해보이긴 하다.😗 물론 구현하다보면 시각적으로 편리한 방식으로 코드로 짜는 것이 중요하다고 생각한다.)

    sayHi(completion: {
        print("2초가 지났다. 1")
    })
    
    sayHi(){
        print("2초가 지났다. 2")
    }
    
    sayHi{
        print("2초가 지났다. 3")
    }

     

    만약 내가 이 클로저 매개변수에 매개변수를 줘야할 때도 다음과 같이 여러 형태로 작성을 할 수 있다.

    나는 이 중 4번째 방식을 다른 캠퍼분들 코드에서 가장 많이 봤던 것 같다.

     

    sayHiWithName(completion: { (comment: String) in
        print("2초 뒤에 그가 말했다! comment: ", comment)
    })
    
    sayHiWithName(completion: { comment in
        print("2초 뒤에 그가 말했다! comment: ", comment)
    })
    
    sayHiWithName{ comment in
        print("2초 뒤에 그가 말했다! comment: ", comment)
    }
    
    sayHiWithName{
        print("2초 뒤에 그가 말했다! comment: ", $0)
    }

    클로저 매개변수를 optional로 구현하여 만약 클로저 매개변수를 실행하고 싶지 않다면,

    다음과 같이 함수에서 미리 클로저를 optional로 구현해두고 필요할 때만 클로저 매개변수를 넣어주면 된다.

    func sayHiOptional(completion: (() -> Void)? = nil){ //기본은 nil로 하겠다.
        print("sayHiOptional() called")
        sleep(2) // 2초 잠깐 멈추기
        // completion 클로저 실행
        completion?() // <-이렇게 해야 optional을 터뜨릴 수 있다.
    }
    
    sayHiOptional() //이러면 클로저를 안넣어주고서 실행할 수 있다. completion?()이 실행되지 않는다.
    
    sayHiOptional(completion: {
        print("2초가 지났다.!!")
    })

    여기서 중요한 것은 함수에서 클로저 매개변수를 구현할 때 optional을 터뜨려야 하기 때문에

    completion?()

    이렇게 해야 한다.

     

    그럼 이번엔 map을 활용해서 배열을 변환해보면 다음과 같다.

    //var transformedNumbers = myNumbers.map { (aNumber: Int) -> String in
    //    return "숫자: \(aNumber)"
    //}
    
    //var transformedNumbers = myNumbers.map { aNumber in
    //    return "숫자: \(aNumber)"
    //}
    
    var transformedNumbers = myNumbers.map {
        return "숫자: \($0)"
    }

    이것도 마찬가지로 형태를 다양하게 축약해서 쓸 수 있다.

    728x90
    반응형