익명 함수
라고 불리는 클로저는 함수와 유사하게 특정 작업을 수행하는 코드블록 입니다.이름
,파라미터
, 반환타입
, 구현부
)에서 이름
을 제외한 파라미터
, 반환타입
, 구현부
로 구성되어 있습니다.데이터타입
으로 클로저를 사용할 수 있습니다.
// 타입으로 사용할 때 표현방법
(파라미터 데이터 타입) -> 리턴 타입
Void
라고 명시합니다.( )
괄호는 있어야 합니다!Optional
타입으로 사용할 수 있습니다.( )
로 감싼 후 ?
를 입력해야 합니다.
let closure: (Int, String) -> Void // 파라미터는 튜플(Int, String) 타입이고
// 반환타입은 없습니다.
// 만약 -> Void가 생략되었다면 튜플과 같죠!?
let closure2: (Int) -> Viod // 파라미터는 1개로 Int이며 반환타입은 없습니다.
let closure3: () -> Void. // 파라미터는 없고 반환타입은 없습니다.
// 파라미터는 ()로 감싸고 있어서 Void를 생략할 수 있어요.
let closure4: (Int) -> Int. // 파라미터는 1개이고 Int 타입이며, 반환값은 Int 입니다.
let closure5: (String, Int) -> Int // 파라미터는 튜플(String, Int) 타입이고
// 반환타입은 Int 입니다.
let optionalClosure: ((Int) -> Int)? // 옵셔널 타입입니다.
// 만약 ()가 없었다면 리턴값이 Int? 라고 판단됩니다!
in
키워드를 사용하여 파라미터와 구현부를 구분해야 합니다.
// 기본적인 구현 방법
// 중괄호로 시작하고 파라미터와 구현부 부분을 in 키워드를 사용하여 나눕니다.
{ 파라미터 이름 in
// 구현부
// 리턴
}
in
키워드를 사용하여 앞에는 파라미터 이름을 뒤에는 구현부를 분리하여 작성합니다.$0
으로 사용할 수 있습니다. ($0
, $1
, $2
...)in
키워드까지 생략해야 합니다.in
을 생략해야 합니다.변수이름?()
형식으로 사용하면 됩니다.{}
뒤에 괄호 ()
를 사용하면 됩니다.이번 강의에서 클로저를 공부하면서 람다가 생각나서 한번 찾아서 정리해보려고 한다.
The Swift language Guide 에서 소개하는 클로저는 다음과 같습니다.
클로저(Closure)
코드블록으로 C와 Objective-C의 블럭(blocks)과 다른 언어의 람다(lambdas)와 비슷합니다.
클로저는 어떤 상수나 변수의 참조를 캡쳐(capture)해 저장할 수 있습니다.
위 두 가지 특징 정도로만 이해해놓고 넘어가보도록 하자...
escaping
은 탈출하다
는 의미@escaping
키워드를 사용합니다.@escaping
키워드를 명시하면 됩니다.non-escaping
입니다.// 정상적인 클로저
func testEsacpingClosure(closure: () -> Void) {
closure()
}
testEsacpingClosure {
print("Hello")
}
// 출력값 : Hello
// 파라미터로 받은 클로저가 함수가 실행된 후 사용되는 경우
import UIKit
func testEsacpingClosure(closure: () -> Void) {
// 나중에 배울 개념으로 1초뒤에 closure를 호출해달라는 의미
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
closure() // Error 발생!
}
}
// 함수가 끝나고 1초뒤에 closure를 호출하기 때문에 오류 발생
//파라미터로 받은 클로저는 다른 변수나 상수에 할당할 수 없습니다.
func testEsacpingClosure(closure: () -> Void) {
let newClosure: () -> Void = closure // Error 발생
}
@escaping
을 사용하여 서버의 응답을 받은 후에 클로저를 호출합니다.value type
의 값도 참조로 캡처하지만 value type
으로 캡처할 수 있습니다.reference type
은 참조방식을 정할 수 있습니다.[ ]
대괄호 안에 사용할 변수나 상수를 작성하여 캡처리스트를 정할 수 있습니다.reference type
참조 방식 정하기strong
이기때문에 강한순환참조가 발생할 수 있습니다!weak
, unowned
사용 가능합니다class Animal {
var name: String
var age: Int
init(name: String, age: Int) {
self.name = name
self.age = age
}
}
func testClosureCaptureReference() {
var animal = Animal(name: "Dog", age: 1)
let closure = { [weak animal] in // [ ]
print(animal?.age)
}
closure() // 출력 값 : Optional(1)
animal.age = 2
closure() // 출력 값 : Optional(2)
}
testClosureCaptureReference()
value type
으로 캡처하는 방법
struct Person {
var name: String
var age: Int
}
func testClosureCapture() {
var person = Person(name: "Brody", age: 20)
var a = 1
let closure = { [person] in
print(person.age)
print(a)
}
closure() // 출력 값 : 20
person.age = 25
a = 2
closure() // 출력 값 : 20
}
testClosureCapture()
/* 출력 값
20 // person은 value type으로 복사를 하였기 때문에 변경이 없음
1 // a는 참조캡처를 하여 값 변경
20 // person은 value type으로 복사를 하였기 때문에 변경이 없음
2
*/