카테고리 없음

swift 클로저에 대해 알아보자

kingarthur 2024. 6. 9. 19:38

오늘의 공부 내용은 클로저다. 

클로저는 = 함수다 

근데 함수라고 표현하면서 또 함수랑 다르게 표현한다.? 

처음에는 먼말인가 했다. 그래서 클로저에 대해 찾아보기로 했다. 

 

난 여기서 내용을 참조했다. 

https://babbab2.tistory.com/81

 

Swift) 클로저(Closure) 정복하기(1/3) - 클로저, 누구냐 넌

안녕하세요 :) 소들입니다 으휴 저번 주도 쓸데없이 바빴어서 포스팅을 못했네용 나태한 저번주의 나를 반성하며.. 하암..🥱 음 전에 제가 Swift의 꽃이 Optional이라고 말한 적 있는데, Optional 만큼

babbab2.tistory.com

 

 

1. 클로저란?

클로저라고 하면 보통 익명함수를 뜻한다고 생각할텐데,

사실 func 키워드를 이용해 이름을 붙여주는 함수들도 모두 클로저임!!

 

무슨말이냐면,

클로저는 다음과 같이 두 가지 종류가 있음

 

 

Named Closure가 있고, Unnamed Closure가 있는데

우리가 다음과 같이 선언해왔던 이름이 있는 함수

 

 

func doSomething() {
    print("Somaker")
}

 

 

Named Closure임! 근데, 이를 클로저라 부르지 않고,

그냥 함수라고 부르는 것 뿐임! (그치만 클로저란 사실은 변함 없음)

 

그리고, 다음과 같이 이름을 붙이지 않고 사용하는 함수

 

 

let closure = { print("Somaker") }

 

 

익명함수, 즉 Unnamed Closure라고 부르는 것임

근데 보통 Closure라고 하면 Unnamed Closure를 말하긴 함 :)

따라서, 정리하자면

 

클로저는 Named Clousre & Unnamed Closure 둘다 포함하지만,

보통 Unnamed Closure를 말한다!

 

 

발췌 끝 :)

 

자, 근데 여기서 한 가지를 더 붙이자면,

클로저도 익명이긴 하지만 함수이기 때문에,

1급 객체 함수의 특성을 고대로 다 갖고 있음!!!

 

뭐 함수 정복하기 포스팅에서 배운 

변수나 상수에 대입하고... 리턴할수 있고.. 파라미터로 받을 수 있고 그런 것들 :)

때문에 클로저 또한 자료형을 가지고 있음!

이제 클로저 사용법에 대해서 봐보자 👀

 

2. 클로저 표현식

 

이번엔, 클로저의 표현식에 대해 살펴 보겠음!

 

 

 

 

표현식은 이렇게 쓰는데,

익명 함수인 만큼 func이란 키워드를 쓰지 않음!!!!!

그리고 클로저는 다음과 같이 클로저 헤드 클로저 바디로 이루어져 있는데

 

 

 

 

이 둘을 바로 구분지어주는 게 바로 in 이라는 키워드임!!!!

 

근데 이렇게만 보면 뭔말인지 이해 1도 안 되고요??

실제 어떤 식으로 사용하는지를 봐보면서 이해를 해보자 :)

 

2-1. Parameter와 Return Type이 둘 다 없는 클로저

 

자, 클로저는 뭐다?? 익명이긴 하지만 함수다!

따라서 Swift에서 1급 객체이기 때문에, 다음과 같이 상수에 클로저를 대입할 수 있음

특히 Parameter와 Return Type이 둘 다 없는 경우는 다음과 같이 사용함

 

 

let closure = { () -> () in
    print("Closure")
}

 

 

고러허다 그러면 의구심이 들 거임

함수처럼 Return Type 없으면 생략해도 되는 거 아님?ㅎㅎ 물론 됨 !!

심지어 Return Type이 있어도 생략 가능하고;; 함수에선 안되는 Parameter조차 생략 가능함;;

 

근데 이건 다음 포스팅인 클로저 경량 문법에서 배울 것이니!

지금은 그냥 교과서적인 표현부터 알고 가겠음 :)

 

 

 

2-2. Parameter와 Return Type이 있는 클로저

 

자, 이런 경우엔 다음과 같이 표시함

 

 

let closure = { (name: String-> String in
    return "Hello, \(name)"
}

 

 

함수와 비슷해 보여서 어려울 것 하나 없지만

한 가지 주의해야 할 것이 있음!!

 

함수 때 배운 대로 Parameter의 "name"은 단독으로 쓰였으니,

Argument Label이자, Parameter Name이겠네여?

 

라고 생각할 수 있지만,

 

클로저에선 Argument Label을 사용하지 않음

 

따라서, name은 Argument Label이 아니고, 오직 Parameter Name임!

따라서 좀이따 보겠지만 다음과 같이 클로저를 호출할 땐

 

 

closure("Sodeul")
closure(name: "Sodeul") //error!

 

 

 Argument Label을 사용하지 않음!

사용하면 에러나염

 

 

자, 이것이 클로저의 표현식 다인데...

어려웠다면 다시 차근차근 읽어보시면 됩니다 :)

 

 

 

 

3. 1급 객체로서 클로저

 

이번엔 1급 객체 함수의 특징을 클로저에 대입시켜 보려고 함

어렵진 않은데 처음 보면 좀 헷갈릴 순 있기에 다뤄봄

 

 

 ⓛ 클로저를 변수나 상수에 대입할 수 있다 

 

음 이건 위에서 봤듯이 

클로저 또한 변수나 상수에 대입할 수 있고

이 대입된 변수나 상수로 실행도 할 수도 있음

 

 

let closure = { () -> () in
    print("Closure")
}

 

 

이런 식으로 대입과 동시에 클로저를 작성할 수도 있고,

또는, 기존에 클로저를 대입한 변수나 상수를

 

 

let closure2 = closure

 

 

이렇게 새로운 변수나 상수에 대입할 수도 있음~_~

 

 

 ② 함수의 파라미터 타입으로 클로저를 전달할 수 있다 

 

 

func doSomething(closure: () -> ()) {
    closure()
}
 

 

 

자, 이런 식으로 함수를 파라미터로 전달받는 doSomething이라는 함수가 있음

이 경우, 파라미터로 함수를 넘겨줘도 되지만

 

 

doSomething(closure: { () -> () in
    print("Hello!")
})
 

 

 

이렇게 클로저를 넘겨줘도 됨!!

이렇게 보니까 헷갈릴 수 있는데 함수 호출을 다음과 같이 생각하면 됨

 

 

 

 

초록색 영역이 클로저로 작성된 부분이고,

이것이 closure란 Argument Label의 Parameter로 전달된 것임!!!

 

그러면 doSomething이란 함수에서 파라미터로 전달받은 함수를 실행하면,

 

 

 

 

우리가 작성한 클로저가 실행 됨:)

 

 

 ③ 함수의 반환 타입으로 클로저를 사용할 수 있다 

 

이또한, 선언부는 기존 함수와 똑같지만,

 

 

func doSomething() -> () -> () {
}
 

 

 

이렇게!!! 

그러나 실제 값을 return할 때 함수가 아닌

 

 

func doSomething() -> () -> () {
    
    return { () -> () in
        print("Hello Sodeul!")
    }
}
 

 

 

이렇게 클로저를 리턴할 수 있음!!!

(문법이 어렵게 느껴질 수 있지만, 다음 포스팅에서 매우 간단해지니 겁먹지 말자..!)

 

또한 호출하는 곳에서 클로저를 받아서 다음과 같이

 

 

let closure = doSomething()
closure()

 

 

실행시킬 수 있음 :)

 

 

 

 

쨔쟌 :-)

 

 

 

 

4. 클로저 실행하기

 

클로저를 실행할 수 있는 방법은 크게 두 가지가 있는데,

 

 

 

4-1. 클로저가 대입된 변수나 상수로 호출하기

 

 

let closure = { () -> String in
    return "Hello Sodeul!"
}

closure()

 

 

이런 식으로 클로저가 대입된 상수 closure를

호출 구문인 ()를 이용해서 실행시킬 수 있음

 

 

 

4-2. 클로저를 직접 실행하기

 

클로저를 변수나 상수에 대입하지 않고 실행시키고 싶다면, (완벽한 일회성)

그땐 클로저를 ( ) 소괄호로 감싸고, 마지막에 호출 구문인 ()를 추가해주면 됨!

 

 

({ () -> () in
    print("Hello Sodeul!")
})()

 

 

 

이렇게!! 그럼 클로저가 

 

 

 

 

호출 된답니댜 :)

 

이렇게 참 잘 정리해 주셨다. 

 

그래서 나는 얼마나 호출 방식을 줄일 수 있는가 정리해 보았다.

func perFormClosure(param: (String) -> Int) {
    param("Swift")
}
//
//문법을 최적화하는 과정 최대한 줄여보자
//1) 타입 추론
perFormClosure(param: {(str: String) in
    return str.count
})

perFormClosure(param: {str in
    return str.count
})

//2) 한줄인 경우, 리턴을 안 적어도 됨

perFormClosure(param: {str in
    str.count
})

//3) 아큐멘트 이름을 축양
perFormClosure(param: {
    $0.count
})

//4) 트레일링 클로저
perFormClosure(param: {
    $0.count
})

perFormClosure() {
    $0.count
}

perFormClosure {$0.count}

마지막은 한줄로 끝이난다!!

엄청나게 줄어들 수 있다.

리턴 값이 한줄 일때만 가능하다. 명심할 것 

그리고 $0 처음에 먼가 했는데 

클로저라고 막 하길레 그게 먼지 이해 안됬지만 이제는 이해된다. 

$0 파라미터라고 생각하면 될 것 같다. 그래서

func payment = { (user:String, amount: Int) in
    code

user, amount 이렇게 파라미터가 두개면 $0 $1 이렇게 부를 수 있다.

 

재정의 하면 차이는 호출방식

이름이 있는 함수나 없는 함수냐 차이고 

보통은 이름없는 함수를 클로저라고 명칭한다. 

실제로는 둘 다 클로저이긴 하다. 

그러니 오해하지 말자!