클로저란?
- 이름이 없는 함수를 의미함
- 왜 이름이 불필요할까? 함수를 실행하는 형태가 아닌 전달하는 형태로 사용하기 때문에 이름이 불필요함
- ex) 파라미터 전달 등 함수가 필요한 순간 클로저로 정의해서 전달
함수와 클로저 형태의 비교
// 함수의 형태
func myFunction() -> Int {
return ...
}
// 클로저의 형태
{() -> Int in
return
}
함수를 1급 객체로 취급함, 함수는 타입!
- 함수를 변수에 할당할 수 있음
- 함수를 호출할 때, 함수를 파라미터로 전달할 수 있음
- 함수에서 함수를 반환할 수 있음
중괄호는 함수
// 아래 두 코드는 동일함을 인지할 수 있어야 함
var sample = {
print("closure")
}
var sample = { () -> () in
print("closure")
}
sample() // closure
함수를 클로저 형태로 변환하기
- 리턴형에 대한 표기 생략 가능
- (컴파일러가 타입 추론이 가능한 경우) 파라미터 타입에 대한 생략도 대부분 가능
func add(a: Int, b: Int) -> Int {
let result = a + b
return result
}
{(a: Int, b: Int) -> Int in
let result = a + b
return result
}
// 클로저는 타입 추론이 되어 return Type 생략 가능
{(a: Int, b: Int) in
let result = a + b
return result
}
// 클로저는 타입이 명확한 경우 파라미터 Type 생략 가능
let result: Int = {(a, b) in
let result = a + b
return result
}
클로저 사용 이유
- 파라미터 전달 등 함수가 필요한 순간 클로저로 정의해서 전달할 수 있음
- 사후 적으로 클로저를 정의해서 전달을 할 수 있음
- 활용도가 커지고 및 커스터마이징이 가능해짐
func utilizeClosure (a:Int, b: Int, closure: (Int) -> Void) {
let result = a + b
closure(result)
}
utilizeClosure(a: 3, b: 5, closure: {result in
print("result : \(result)")
}
클로저의 문법 최적화
- 실제 사용 시 읽기 쉽고 간결한 코드 작성을 위해 축약된 형태의 문법을 제공함
- 문맥 상에서 파라미터와 리턴 밸류를 추론함
- 코드가 한 줄인 경우(싱글 익스프레션), return을 생략할 수 있음
- 아규먼트 이름을 $0(첫번째 파라미터), $1(두번째 파라미터)과 같은 형태로 축약할 수 있음
- 트레일링 클로저 문법 : 함수의 마지막 인자로 클로저가 전달되는 경우 소괄호 생략 가능
트레일링 클로저 문법
func closureParamFunc(closure: () -> ()) {
closure()
}
// 아래 함수들 호출 방식은 동일함
closureParamFunc(closure: {
print("sample")
})
closureParamFunc(){
print("sample")
}
// 아래 문법 방식이 후행 클로저 문법
closureParamFunc{
print("sample")
}
멀티플 트레일링 클로저 (Swift 5.3)
func multipleClosure(first: () -> (), second: () -> (), third: () -> ()) {
first()
second()
third()
}
// Swift 5.3 이전
multipleClosure(first: {}, second: {}){}
// Swift 5.3 이후
multipleClosure{} second : {} third: {}
클로저의 메모리
- 참조형식으로 Heap (주소를 Stack에 저장)
- 클로저가 실제 실행되는 것은 Stack 프레임에서 동작함
클로저의 캡처
- 클로저에서 저장할 필요가 있는 값을 캡쳐해서 (인스턴스와 비슷한 방식) 값의 주소를 저장함
- 중첩함수 : 내부 함수를 리턴하는 경우 클로저에서 발생하는 동일한 캡쳐현상이 발생함
func calculateFunc() -> ((Int) -> Int) {
var sum = 0
func square(num: Int) -> {
sum += (num * num)
return sum
}
return square
}
var sqaureFunc = caculateFunc()
sqaureFunc(10) // 100
sqaureFunc(20) // 500
sqaureFunc(30) // 1400
calculateFunc()(10) // 100
calculateFunc()(20) // 400
calculateFunc()(30) // 900
@escaping 키워드
- 원칙적으로는 함수의 실행이 종료되면 파라미터로 쓰이는 클로저도 종료됨
- 단, @escaping 키워드를 사용하면 클로저를 종료하지 않고 함수에서 탈출시켜 함수가 종료되어도 클로저는 존재하도록 함
- 사용되는 경우
- 함수 내부에서 사용된 클로저를 외부 변수에 저장하는 경우
- GCD 비동기 코드
func aFunc: () -> () {
print("a")
}
func bFunc(closure: @escaping () -> ()) {
var saveFunc = closure
}
bFunc()
saveFunc() // a
@autoclosure 키워드
- 파라미터가 없는 클로저에만 사용 가능
- @non-esacaping
- 사용하는 경우
- 일반적으로 클로저로 써도 되지만 번거로운 경우 사용
- 번거로움을 해결해주지만, 실제 코드가 명확해보이지 않을 수 있어 사용 지양
- 읽기위한 문법, 잘 사용하지 않음
func aFunc(closure: @autoclosure () -> Bool ) {
if closure() {
print("true")
} else {
print("false")
}
}
var test = aFunc(closure: true)
// @autoclosure 키워드가 붙어있어 자동으로 중괄호를 붙여 자동으로 클로저를 만들어 줌
var test = aFunc(closure: {true})
728x90
'FRONT-END > iOS' 카테고리의 다른 글
Swift 메모리 관리(ARC) (0) | 2023.08.06 |
---|---|
Swift 고차함수 (map, filter, reduce) (0) | 2023.08.04 |
iOS 앱이 만들어지는 원리 (Web과 비교) (0) | 2023.08.02 |
Swift의 self vs Self 이해하기 (0) | 2023.08.02 |
Swift 중첩 타입 (Nested Types) (0) | 2023.08.02 |
댓글