Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- cocoapods
- 어댑터패턴
- 싱글턴패턴
- 전략패턴
- Lifecycle
- 데코레이터패턴
- 스트래터지패턴
- 옵저버패턴
- 컴파운드패턴
- Xcode
- SWIFT
- unowned
- Scenedelegate
- 컴포지트패턴
- 프록시패턴
- 상태패턴
- 커맨드패턴
- 추상팩토리패턴
- 팩토리메서드패턴
- ios
- 파사드패턴
- DispatchQueue
- Mobile
- ViewController
- RxSwift
- WKWebView
- 디자인패턴
- 템플릿메서드
- 스테이트패턴
- 이터레이터패턴
Archives
- Today
- Total
ios dev kangwook.
Swift) Closure 본문
iOS개발을 하다보면 Closure는 필수적으로 알아야하는 개념이라고 할 수 있다.
이 Closure가 뭔지 확실하게 알고 넘어가야할 것 같아서 정리를 하려고한다.
Closure
일반적으로 Closure는 익명함수라고 알고 있는 사람들이 많을 것 같다.
왜냐면 보통 다 그렇게 생겼음 얘들이..
근데 이게 사실 그렇지가 않고 일반적으로 우리가 아는 함수들도 다 Closure 의 한 형태라고 생각하면 될 것 같다.
그럼 하나씩 살펴보도록 하자.
기본 클로저
{(매개변수들) -> 반환 타입 in 실행코드}
- 클로저를 사용하지 않았을 때와 사용했을 때의 코드 비교
func backwards(first: String, second: String) -> Bool {
return first > second
}
let reversed: [String] = names.sorted(by: backwards)
let reversed: [String] = names.sorted(by: {(first: String, second: String) -> Bool in
return first > second
})
클로저에 조금 익숙해져서 그런가 요녀석들이 훨씬 간결하게 변한걸 볼 수 있다.
후행 클로저
- 기본 클로저를 좀 더 읽기 쉽게 바꾼 것
- 함수의 괄호가 닫힌 뒤 사용한다.
- sorted(by:)처럼 클로저 단 한 개 만을 전달 인자로 전달하는 경우에는 함수 뒤의 괄호마저 생략 가능하다.
let reversed: [String] = names.sorted(){(first: String, second: String) -> Bool in
return first > second
}
클로저 표현의 간소화
- 메서드의 전달인자로 전달하는 클로저의 경우에 요구하는 타입과 동일해야만 전달이 가능
- 따라서 전달인자로 사용되는 클로저의 경우 자동으로 적합한 타입을 준수했다고 판단해서 리턴 값의 타입을 명시해줄 필요가 없다!
let reversed: [String] = names.sorted{(first, second) in
return first > second
}
상당히 간결하게 변한걸 확인할 수 있겠다.
그런데 심지어 여기서 더 단축시킬 수 있다.
- 클로저는 매개변수의 이름을 간결하게 단축 인자로 변경 가능
- 첫 번째 전달 인자부터 $0, $1, $2 ...
- 실행 코드를 in을 통해 구분해줄 필요성이 사라졌으므로 in 또한 생략이 가능하다.
let reversed: [String] = names.sorted {
return $0 > $1
}
- 반환 값을 갖고 실행문이 단 한 줄일 경우 실행문 = 반환 값이므로 return 생략 가능
let reversed: [String] = names.sorted{ $0 > $1 }
이렇게까지 줄여진다.
값 획득
- 클로저는 자신이 정의된 위치 주변의 상수나 변수의 값을 자신의 내부에서 참조하거나 수정할 수 있다.
func makeIncrementer(forIncrement amount: Int) -> (() -> Int) {
var runningTotal = 0
func incrementer() -> Int {
runningTotal += amount
return runningTotal
}
return incrementer
}
// incrementer 함수가 makeIncrementer 함수 외부로 빠졌을 경우 오류 발생
let incrementByTwo: (() -> Int) = makeIncrementer(forIncrement: 2)
let first: Int = incrementByTwo()
let second: Int = incrementByTwo()
let third: Int = incrementByTwo()
// 클로저는 참조타입으로 메모리 주소를 할당해 줌
탈출 클로저
- 비동기 작업을 하는 함수들은 클로저를 completion handler의 전달인자로 받는데,
- 이 때 사용된 클로저는 함수가 종료된 후 호출을 받는데 @escaping 키워드를 사용하여 탈출이 가능한 클로저임을 명시한다.
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
completionHandlers.append(completionHandler)
}
typealias VoidVoidClosure = () -> Void
let firstClosure: VoidVoidClosure = {
print("Closure A")
}
let secondClosure: VoidVoidClosure = {
print("Closure B")
}
// first와 second 매개변수 클로저는 함수의 반환 값으로 사용될 수 있으므로 탈출 클로저
func returnOneClosure(first: @escaping VoidVoidClosure, second: @escaping VoidVoidClosure, shouldReturnFirstClosure: Bool) -> VoidVoidClosure {
return shouldReturnFirstClosure ? first : second // 클로저를 반환
}
// 함수에서 반환한 클로저가 함수 외부의 상수에 저장됨
let returnedClosure: VoidVoidClosure = returnOneClosure(first: firstClosure, second: secondClosure, shouldReturnFirstClosure: true)
returnedClosure()
var closures: [VoidVoidClosure] = []
// closure 매개변수 클로저는 함수 외부의 변수에 저장될 수 있으므로 탈출 클로저
func appendClosure(closrue: @escaping VoidVoidClosure) {
closures.append(closure)
}
- 위에서 first와 second는 returnOneClosure의 반환 값으로 사용되지만 returnOneClosure 함수가 종료될 때까지 실행이 되지 않아 탈출 클로저라는 것을 알 수 있다.
- 이 경우 @escaping 키워드가 없으면 코드는 에러가 나게 되고,,
- 클로저는 클로저 내부에서 프로퍼티나 메서드 등에 접근할 때 self 키워드를 써줘야한다.
- 비탈출 클로저에서의 self는 선택사항이다!
withoutActuallyEscaping
- 전달한 비탈출 클로저가 탈출 클로저인 척 해야하는 경우가 간혹 있다..
func hasElements(in array: [Int], match prediction: (Int) -> Bool) -> Bool {
return (array.lazy.filter{ predicate($0) }.isEmpty == false)
}
- 요런 함수에서 match 매개변수 뒤에 @escaping 키워드가 없으므로 탈출 클로저가 아닌데 아래 코드를 보면
let numbers: [Int] = [2, 4, 6, 8]
let eveneNumberPredicate = {(number: Int) -> Bool in
return number % 2 == 0
}
let oddNumberPredicat = {(nubmer: Int) -> Bool in
return number % 2 == 1
}
func hasElements(in array: [Int], match predicate: (Int) -> Bool) -> Bool {
return withoutActuallyEscaping(predicate, do: { escapablePredicate in
return (array.lazy.filter{ escapablePredicate($0) }.isEmpty == false)
})
}
let hasEvenNumber = hasElements(in: numbers, match: evenNumberPredicate)
let hasOddNumber = hasElements(in: numbers, match: oddNumberPredicate)
print(hasEvenNumber) // true
print(hasOddNumber) //false
- 사용된 filter메서드는 lazy로 설정되어 있고 lazy 컬렉션은 비동기 작업 시 사용되므로 filter 메서드는 탈출 클로저로 전달해주어야 한다.
- 이런게 에러를 유발한다..!
'Swift' 카테고리의 다른 글
Swift) Property (0) | 2022.09.25 |
---|---|
Swift) Semaphore (0) | 2022.09.25 |
Swift) DispatchQueue (0) | 2022.09.22 |
Swift) Concurrency, GCD (0) | 2022.09.20 |
Swift) Opaque Type (0) | 2022.09.11 |
Comments