티스토리 뷰

Swift

Swift [Closure 1부]

이도형 2023. 3. 1. 14:45

들어가기에 앞서...

Swift는 객체지향 프로그래밍 언어이자, 프로토콜지향 프로그래밍언어이며, 함수형프로그래밍 언어다.

클래스와 구조체 단원을 정리하며 객체지향임을 알 수 있었고, 프로토콜 단원을 공부하며, 프로토콜 지향 언어인 것도  알 수 있었다.

이제는 클로저(Closure)를 공부하며 Swift가 함수형 프로그래밍 언어임에 대해 이해해볼 차례다.

물론 초반부에서 함수에 대한 설명을 했지만, 클로저를 알아야 함수형 프로그래밍 패러다임 스타일을 더 명확하게 이해할 수 있다.

개인적으로 Swift를 공부하면서 가장 낯설고, 어려웠던 부분이다. 기본 클로저 표기 문법들은 연습하면 금방 익숙해지지만, 메모리관리와 참조에 관한 부분이 섞여있어 한번에 이해하기엔 무리가 있는 개념이다. 하지만 너무나 중요한 부분이고, 클로저는 앱을 만들때에도 필수적으로 사용되는 문법이기에 반드시 알아야한다.

(클로저에 대해 잘 알지 못한다면, 실제로 앱 제작시, 자신이 치는 코드가 무엇을 의미하는지 모를 수도 있다...) 

 

Index

1. 클로저란?

2. 일급 객체 클로저

3. 클로저의 경량문법(클로저 문법 최적화)

4. 멀티플 트레일링 클로저

5. 마무리

 

 

1. 클로저란?

클로저는 이름이 없는 함수로 익명 함수(Unnamed Function)를 의미한다.

지금까지 알았던 func키워드를 이용한 함수는 이름이 있는 함수(Named Function)였으며,

클로저는 이둘을 모두 포함하는 가장 넓은 범위의 함수(Function)를 칭한다.

=> 클로저와 함수의 기능은 완전히 동일, 이름이 없는 코드 묶음을 클로저(Closure)라고한다.

(이름이 있는 코드 묶음을 함수라고 칭해온 것)

 

// 함수의 형태

func aFunction(str: String) -> String {
    return "Hello, \(str)"
}

aFunction(str: "james") //"Hello, james"

// 클로저의 형태

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

h("james") //"Hello, james"

 

 

2. 일급 객체 클로저

 

클로저는 함수와 마찬가지로 일급객체 취급한다.

함수때 적었던 일급객체의 3가지 조건을 그대로 주어만 (함수 -> 클로저) 바꾸어 적으면

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

2. 함수의 파라미터로 클로저를 전달할 수 있다.

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

 

 

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

 

함수를 변수에 대입했던 방식(복습)

func aFunction1(_ param: String) -> String {
    return param + "!!!!"
}

func aFunction2(name: String) -> String {
    return name + "????"
}

// 함수를 변수에 할당가능(변수가 함수를 가르키게 됨)

var a: (String) -> String = aFunction1
a("안녕") //안녕!!!!

a = aFunction2
a("hello") //hello????

클로저를 변수에 대입하는 방식

//예시 1

//정식문법
let aClosureType: () -> () = {
    print("안녕")
}

//간편문법1
//let aClosureType = { () -> () in
//    print("안녕")
//}

//간편문법2
//let aClosureType = { print("안녕") }   // () -> ()

aClosureType() //안녕

//클로저가 하나의 타입으로 인식된다
type(of: aClosureType) //() -> ()

//예시 2
let closureType = { (param: String) -> String in        
    return param + "@@@@"
}

closureType("스티브") //스티브@@@@

 

2. ⭐️함수의 파라미터로 클로저를 전달할 수 있다.⭐️

// 1) (클로저를 파라미터로 받는 함수)정의

func closureParamFunction(closure: () -> ()) {
    print("프린트 시작")
    closure()
}

// 2)파라미터로 사용할 클로저 정의

// 클로저를 정의
let printSwiftClosure = { () -> () in      // 클로저를 정의
    print("프린트 종료")
}
//let printSwiftClosure = { print("프린트 종료") }

closureParamFunction(closure: printSwiftClosure)
//프린트 시작
//프린트 종료

함수를 실행할때 클로저형태로 전달하는 것은 클로저를 사용하는 주된이유가 된다. WHY?

본래 정의된 함수를 실행시키면서, 클로저를 사후적으로 정의할 수 있어 활용도가 늘어난다.

이를 콜백함수라고도 부른다

콜백함수: 간단히 말하면 파라미터로 함수를 전달하는 것으로,

함수를 실행(호출)하면서 파라미터로 전달하는 함수

아래 예제를 보자.

//예제 1
let param : (Int, Int) -> Int = {(a,b) in return (a + b)}

func myClosureParam(a: Int, b: Int, onlyClosure: (Int, Int) -> Int) {
    print("계산중...")
    print("결과: \(onlyClosure(a,b))")
}

myClosureParam(a: 2, b: 5, onlyClosure: param)
// 계산중...
// 결과: 7

//예제2
let sayMyName: (String) -> () = {name in print("\(name) \(name) \(name)!!!")}

func nameParam(name: String, param: (String) -> ()) {
    param(name)
}

nameParam(name: "Steve", param: sayMyName)

//Steve Steve Steve!!!

//예제3

//파라미터로 사용할 클로저 2개 정의
let print1 = {
    print("1")
}

let print2 = {
    print("2")
}


func multiClosureFunction(closure1: () -> Void, closure2: () -> Void) {
    closure1()
    closure2()
}

multiClosureFunction(closure1: print1, closure2: print2) 
// 1, 2

func multiClosureFunction4(closure1: () -> Void, closure2: () -> Void) {
    closure1()
    print(15)
    closure2()
}

multiClosureFunction4(closure1: { print("10") }, closure2: { print("20") }) 
//10 15 20

 

 

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

//Void에서 Void를 리턴하는 클로저를 () -> () 라고함
func doSomething() -> (() -> ()) {
    return { () -> () in print("doSomething!!")}
}

let closure = doSomething() //doSomething의 실행후 결과값이 담기기에 doSomething!!을 출력하는 클로저가 담겨있다
closure() //doSomething!!

let pp = doSomething //함수자체를 담는다면?
pp()() //doSomething함수 호출(), 내부클로저 호출() 2번을 해줘야 doSomething!! 출력

 

클로저를 변수나 상수에 담지 않고 직접 실행하기

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

//Hello Sodeul!

 

3. 클로저의 경량문법(문법 최적화)

1. Trailing closure(트레일링 클로저): 함수의 마지막 파라미터가 클로저일때, 소괄호를 생략가능

2. Type Inference: 문맥상에서 파라미터와 반환값의 타입 추론

3. Implicit Return: 싱글 익스프레션(한줄)일 경우, 리턴 표기 생략가능

4. Shorthand Argement Names(아규먼트 이름 축약): $0, $1.  in 키워드 생략가능

 

1 .트레일링 클로저에 대한 이해

// 1) (클로저를 파라미터로 받는 함수)정의

func closureParamFunction(closure: () -> Void) {
    print("프린트 시작")
    closure()
}

// 2) 함수를 실행할때 클로저 형태로 전달
// 함수의 마지막 전달 인자(아규먼트)로 클로저 전달되는 경우, 소괄호를 생략 가능

closureParamFunction(closure: {
    print("프린트 종료")
})

closureParamFunction(closure: ) {      // 소괄호를 앞으로 가져오기
    print("프린트 종료")
} //프린트 시작, 프린트 종료

closureParamFunction() {               // closure: 아규먼트 생략가능
    print("프린트 종료")
} //프린트 시작, 프린트 종료


// 소괄호를 아예 생략할 수 있다.(최종본)
// => 아래 형태가 함수를 실행하고 마지막 아규먼트로 클로저를 전달했다는 형태에 익숙해져야함

closureParamFunction {
    print("프린트 종료")
}

 

경량문법 4가지 

func doSomething(closure: (Int, Int, Int) -> Int) {
    closure(1, 2, 3)
}

//1. Type Inference (파라미터 타입과 리턴 타입 생략)
doSomething(closure: { (a, b, c) in
    return a + b + c
})

//2.Shorthand Argement Names (아규먼트 이름 축약, in키워드 생략)
doSomething(closure: {  
    return $0 + $1 + $2
})

//3. Implicit Return (단일 리턴문(한줄)일 경우, return키워드 생략 )
doSomething() {  
     $0 + $1 + $2
}

//4. Trailing Closure (마지막 파라미터가 클로저인 트레일링 클로저 소괄호 생략)
doSomething {  
     $0 + $1 + $2
}

 

추가 연습 예제

// 기본 함수의 정의
func performClosure(param: (String) -> Int) {
    param("Swift")
}

// 최종본: performClosure { $0.count }

// 문법을 최적화하는 과정

// 1) Type Inference
performClosure(param: { (str: String) in
    return str.count
})
performClosure(param: { str in
    return str.count
})

// 2)Implicit Return
performClosure(param: { str in
    str.count
})

// 3) Shorthand Argements Names
performClosure(param: {
    $0.count
})

// 4) Trailing Closure
performClosure(param: {
    $0.count
})
performClosure() {
    $0.count
}
//최종본
performClosure { $0.count }

 

4. 멀티플 트레일링 클로저 - Swift 5.3이후

func multipleClosure(first: () -> (), second: () -> (), third: () -> ()) {
    first()
    second()
    third()
}

// 기존 방식에서는 마지막 클로저만 트레일링 클로저로 쓸 수 있었음
// (클로저의 경계에서 코드가 헷갈릴 가능성이 있었음)

multipleClosure(first: {
    print("1")
}, second: {
    print("2")
}) {
    print("3")
}

 

멀티플 트레일링 클로저 실사용 예시

실제로 앱을 만들때 사용할 클로저 예시

 

 

5. 마무리

 

클로저의 가장 기본 문법.  경량문법이 있기때문에, 예제를 만들어보고 연습하면 금방 익숙해질 부분들.

이제 캡쳐, escaping키워드, 강한/약한 참조 등 보다 어렵고 중요한 부분들을 정리할 차례나

먼저 Swift의 메모리관리방식 ARC에 대해 정리할 필요가 있다

 

출처

Inflearn Allen님의 강의 노트

Swift 공식문서

야곰님의 스위프트 프로그래밍 3

 

경량문법 예제 참고

https://babbab2.tistory.com/82

 

Swift) 클로저(Closure) 정복하기(2/3) - 문법 경량화 / @escaping / @autoclosure

안녕하세요 :) 소들입니다 이번 포스팅인 저번 포스팅에 이어 클로저에 대해 다뤄보려고 해요!!! 저번 포스팅이 클로저의 표현식과 특징?에 대해 알아봤었어요!!! 근데 생각보다 클로저의 구문은

babbab2.tistory.com

 

반응형

'Swift' 카테고리의 다른 글

Swift [Closure 2부]  (0) 2023.03.04
Swift [ARC- strong, memory leak, weak, unowned]  (0) 2023.03.02
Swift [Protocol 2부]  (0) 2023.02.27
Swift [Protocol 1부]  (0) 2023.02.26
Swift [Extension]  (0) 2023.02.26
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함