[Swift] 클로저
클로저(closure)
- C언어와 Objective-C의 block과 lamda와 유사
- 일정 기능을 하는 코드를 하나의 블록으로 모아놓은 것을 말함
- 변수나 상수가 선언된 위치에서 참조를 획득하고 저장할 수 있음
- iOS에서 클릭 이벤트를 실행할 때 사용
- 클로저의 형태
- 이름이 있으면서 어떤 값도 획득하지 않는 전역함수의 형태
- 이름이 있으면서 다른 함수 내부의 값을 획득할 수 있는 중첩된 함수의 형태
- 이름이 없고 주변 문맥에 따라 값을 획득할 수 있는 축약 문법으로 작성된 형태
클로저 표현 방법
- 클로저의 위치에 따라 기본 클로저와 후행 클로저로 구분
- 각 표현내에서 가독성을 해치지 않는 선에서 표현을 생략하거나 축약 가능
- 기본 클로저의 표현 방법
{ ( 매개변수들 ) -> 반환 타입 in
실행 코드
}
- 함수와 마찬가지로 입출력 매개변수를 사용 가능
- 매개변수의 이름을 지정한다면 가변 매개변수도 사용 가능
- 매개변수의 기본값은 사용 불가
- 기본 클로저 사용
func printMyName(name1: String, name2: String)-> Void{
print("name1:\(name1), name2:\(name2)")
}
printMyName(name1: "홍길동", name2: "변사또")
// name1: 홍길동, name2: 변사또
var myClosure: (String, String) -> Void = {
(name1: String, name2: String) -> Void in
print("name1:\(name1), name2:\(name2)")
}
myClosure("홍길동", "변사또")
// name1: 홍길동, name2: 변사또
후행 클로저(Tralling Closure)
- 클로저가 조금 길어지거나 가독성이 조금 떨어질 때 사용
- Xcode에서 자동완성 기능을 사용하면 자동으로 후행 클로저를 유도
- 맨 마지막 전달인자로 전달되는 클로저에만 해당됨
→ 전달인자로 클로저 여러 개를 전달할 때는 맨 마지막 클로저만 후행 클로저로 사용가능
- 매개변수에 클로저가 여러 개 있는 경우 → 다중 후행 클로저 문법 사용 가능
- 후행 클로저 사용
func calc(a: Int, b: Int, method: (Int, Int) -> Int) -> Int{
return method(a, b)
}
var result = calc(a: 10, b: 20, method: {
(a: Int, b: Int) -> Int in
return a + b // 30
})
result = calc(a: 10, b: 20){
(a: Int, b: Int) -> Int in
return a + b // 30
}
클로저 표현 간소화
- 문맥을 이용한 타입 유추 : 매개변수의 타입이 생략됨
let reversed: [String] = names.sorted{(first, second) in
return first > second
}
- 단축 인자 이름 : 첫 번째 전달인자부터 $0, $1, $2, $3, ··· 순서로 $와 숫자의 조합으로 표현
let reversed: [String] = names.sorted{
return $0 > $1
}
→ 매개변수 및 반환 타입과 실행 코드를 구분하기 위해 사용했던 키워드 in 사용할 필요 없음
- 암시적 반환 표현 : return 키워드 생략 가능
let reversed: [String] = names.sorted{ $0 > $1 }
- 연산자 함수 : 매개변수의 타입과 반환 타입이 연산자를 구현한 함수의 모양과 동일하다면 연산자만 표기하더라도 알아서 연산하고 변환
let reversed: [String] = names.sorted(by: >)
- 값 획득 : 자신이 정의된 위치의 주변 문맥을 통해 상수나 변수를 획득(Capture) 가능함
→ 이것을 통해 주변에 정의한 상수나 변수가 덩 이상 존재하지 않더라도 해당 상수나 변수의 값을 자신 내부에서 참조하거나 수정가능
→ 클로저가 비동기 작업에 많이 사용됨, 비동기 콜백을 작성하는 경우에 현재 상태를 미리 획득해두지 않으면
실제로 클로저의 기능을 실행하는 순간에는 주변의 상수나 변수가 이미 메모리에 존재하지 않는 경우가 발생
- 중첩 함수도 하나의 클로저 형태 → 자신을 포함하는 함수의 지역변수나 지역상수를 획득할 수 있음
클로저는 참조 타입
- 함수나 클로저를 상수나 변수에 할당할 때마다 사실은 상수나 변수에 함수나 클로저의 참조를 설정하는 것
탈출 클로저(Escaping Closure)
- 함수의 전달인자로 전달한 클로저가 함수 종료 후에 호출 될 때 클로저가 함수를 탈출(Escape)한다고 표현
- 클로저를 매개변수로 갖는 함수를 선언할 때 매개변수 이름의 콜론(:) 뒤에 @escapeing 키워드 사용하여
클로저가 탈출을 허용한다고 명시해줄 수 있음
- 비동기 작업으로 함수가 종료되고 난 후 호출할 필요가 있는 클로저를 사용해야 할 때 탈출 클로저가 필요함
- 정렬할 요소를 비교 연산하기 위해 전달인자로 전달하는 클로저는 비탈출 클로저(Nonescape Closure)
- @escaping 키워드를 따로 명시하지 않는다면 매개변수로 사용되는 클로저는 기본으로 비탈출 클로저임
- 타입 내부 메서드의 매개변수 클로저에 @escaping 키워드를 사용하여 탈출 클로저임을 명시
→ 클로저 내부에서 해당 타입의 프로퍼티나 메서드, 서브스크립트에 접근하려면 self 키워드를 명시적으로 사용
- 비탈출 클로저 내부에서 self 키워드는 선택사항
- withoutActuallyEscaping(_:do:) : 클로저를 탈출 클로저 인양 사용할 수 있게 돕는 함수
- 클래스 인스턴스 메서드에 사용되는 탈출, 비탈출 클로저
typealias VoidVoidClosure = () -> Void
func functionWithNoescapeClosure(losure: VoidVoidClosure){
closure()
}
func functionWithEscapingClosure(completionHandler: @escaping
VoidVoidClosure) -> VoidVoidClosure{
return completionHandler
}
class SomeClass{
var x = 10
func runNoescapeClosure(){
// 비탈출 클로저에서 self 키워드는 선택사항
functionWithNoescapeClosure{ x= 200 }
}
func runEscapeingClosure() -> VoidVoidClosure {
// 탈출 클로저에서는 명시적으로 self를 사용해야함
return functionWithEscapingClosure { self.x = 100 }
}
}
let instance: SomeClass = SomeClass()
instance.runNoescapeClosure()
print(instance.x) // 200
let returnedClosure: VoidVoidClosure = instance.runEscapingClosure()
returnedClosure()
print(instance.x) // 100
자동 클로저(Auto Closure)
- 함수의 전달인자로 전달하는 표현을 자동으로 변환해주는 클로저
- 자동 클로저는 전달인자를 갖지 않음
- 클로저가 호출되기 전까지 클로저 내부의 코드가 동작하지 않음 → 연산 지연시킬 수 있음
- 연산에 자원을 많이 소모한다거나 부작용이 우려될 때 유용하게 사용 가능 → 코드의 실행을 제어하기 좋음
- 클로저를 사용한 연산 지연
var customersInLine: [String] = ["a", "b", "c", "d"]
print(custmersInLine.count) // 4
let customerProvider: () -> String = {
return customersInLine.removeFirst() // "a"
}
print(customersInLine.count) // 4
print("now serving \(customerProvider()!") // now serving a!
print(customerInLine.count) // 3
- 클로저가 영영 호출되지 않는다면 내부의 코드도 실행되지 않기 때문에 해당 연산은 실행되지 않음
- 같은 조건의 클로저를 함수의 전달인자로 전달하는 예제
var customersInLine: [String] = ["a", "b", "c", "d"]
func serveCustomer(_ customerProvider: () -> String){
print("now serving \(customerProvider()!")
// now serving a!
}
serveCustomer({customersInLine.removeFirst()}) // a
- 코드의 serveCustomer(_:) 함수는 클로저를 매개변수로 전달받고 있음
- 자동 클로저는 전달인자를 갖지 않는 반환 타입의 값이 자동 클로저의 매개변수로 전달되면 이를 클로저로 바꿔줄 수 있는 것임
- 자동 클로저의 과도한 사용은 코드를 이해하기 어렵게 만듦
참조 : Swift 스위프트 프로그래밍 3판 Swift 5, 저자 야곰 (한빛미디어)
인프런 강의 (iOS 최신 앱 개발 강의 - 기초부터 핵심 요소, 간단한 포폴까지 제작)
'iOS > Swift' 카테고리의 다른 글
[Swift] UnsafePointer, UnsafeMutablePointer (0) | 2023.01.08 |
---|---|
[Swift] 소수점 없애기 (0) | 2023.01.01 |
[Swift] 다른 앱 실행시키기 (0) | 2022.12.04 |
[Swift] URLComponent (0) | 2022.11.20 |
[Swift] URLSession https 인증서 신뢰 (0) | 2022.11.20 |