클로저
- 다른언어의 람다(Lambda)와 유사
- 일정 기능을 하는 코드를 하나의 블록으로 모아놓은 것
- 변수, 상수가 선언된 위치에서 참조(Reference)를 획득(capture)하고 저장할 수 있음
- 형태
- 이름 O, 획득 X : 전역함수의 형태
- 이름 O, 획득 O : 중첩함수의 형태
- 이름 X, 획득 O : 축약문법으로 작성한 형태
func backwards(A: String, B: String) -> Bool {
return A > B
}
let arr = names.sorted(by:backwards)
// 아래는 전부 같은 클로저
let arr = names.sorted(by: { (A: String, B: String) -> Bool in
return A > B
}) // 기본 클로저
let arr = names.sorted(){ (A: String, B: String) -> Bool in
return A > B
} // 후행 클로저
let arr = names.sorted { (A: String, B: String) -> Bool in
return A > B
} // 소괄호 생략
let arr = names.sorted { (A, B) in
return A > B
} // 타입유추(문맥상)
let arr = names.sorted { return $0 > $1 } // 단축인자
let arr = names.sorted { $0 > $1 } // 암시적반환(단, 한줄일때만 가능)
let arr = names.sorted(by: >) // 연산자함수 : 매개변수와 반환타입이 연산자를 구현한 함수의 모양과 동일하면 연산자만 표기해도 알아서 연산하고 반환할 수 있음
값 획득
- 주변에 정의한 상수나 변수가 더 이상 존재하지 않더라도 참조하거나 수정할 수 있음(비동기 작업에서 많이 사용하기 때문)
- 아래의 예시에서 first1과 first2가 각각 2,10의 값을 가질수 있었던 것은 A와 B가 각각 total의 참조를 미리 획득했기 때문
func makeInc(_ amount: Int) -> (()->Int) {
var total = 0
func inc() -> Int {
total += amount
return total
}
return inc
}
let A: (()->Int) = makeInc(2)
let B: (()->Int) = makeInce(10)
let first1: Int = A() // 2
let second1: Int = A() // 4
let first2: Int = B() // 10
let second2: Int = B() // 10
옵셔널 체이닝
- nil일지도 모르는 프로퍼티, 메서드, 서브스크립트 등을 가져오거나 호출할 때 사용할 수 있는 일련의 과정
- 중첩된 옵셔널 중 하나라도 값이 존재 하지않을 경우 nil을 반환
struct Address {
var city: String
var building: String?
}
struct Human {
var name: String
var age: Int
var address: Address?
init(name: String, age: Int){
self.name = name
self.age = age
}
}
let peopleA: Human(name: "AAA", age: 25)
if let build = peopleA.address?.building { // 체이닝을 사용하면 address가 있는지 building이 있는지 나눠서 확인하지 않아도 된다
print("\(peopleA.name)의 빌딩 이름은 \(build)입니다")
}else {
print("빌딩이 없습니다")
}
빠른종료(guard)
- if 구문과 반대로 조건이 false일때 작동하는 문법, 예외처리를 해야하는 상황에 사용한다
- 기본 형태 : guard 조건 else { }
- return, break, continue, throw 등의 제어문 전환 명령어를 쓸 수 없는 상황에서는 사용이 불가능하다
func inputCheck(input: String?) {
guard let result: String = input else {
print("input is nil (error)")
return
}
print("input is \(result) (not error)")
}
맵, 필터, 리듀스
- 스위프트에서는 함수를 일급 객체로 취급 (함수를 다른 함수의 전달인자로 사용가능)
- 고차함수 : 매개변수로 함수를 갖는 함수 (맵, 필터, 리듀스 등)
- 고차함수를 효율적으로 사용하면 코드가 간단해 질 수 있음
맵(Map)
- 매개변수로 전달된 함수를 실행하여 그 결과를 다시 반환하는 함수
- 배열, 딕셔너리, 세트, 옵셔널 등에서 사용할 수 있음 (Sequence, Collection 프로토콜에 따르는타입과 옵셔널)
- 새로운 컨테이너가 생성되서 반환 - 기존 데이터변형(transform)할때 많이 사용
- 다중 스레드 환경에서 동시에 값이 변하려고 할때, 예측하지 못한 결과가 발생하는 것을 방지해줌 (안정성보장)
필터(filter)
- 컨테이너 내부의 값을 걸러서 추출
- 특정 조건에 맞게 걸러내는 역할, 매개변수로 전달되는 함수의 반환타입은 Bool타입
리듀스(reduce)
- 컨테이너 내부의 콘텐츠를 하나로 합하는 기능을 실행 (결합 기능)
- A형태 : 각 요소를 전달받아 연산한 후 값을 다음 클로저 실행을 위해 반환하여 컨테이너를 순환하는 형태
- B형태 : 컨테이너를 순환하며 클로저가 실행되지만, 클로저가 따로 결과값을 반환하지 않는 형태 (대신 inout 매개변수를 사용하여 초깃값에 직접연산을 실행)
let number1: [Int] = [1,2,3,4]
//map
let number2: [Int] = number1.map{ $0 * 2 } // [2,4,6,8] - 클로저에서 나온 축약 형태
//filter
let number3: [Int] = number1.filter{ $0 % 2 == 0 } // [2,4] - 조건이 true면 통과
//reduce
let number4: [Int] = number1.reduce(0, { (result: Int, next: Int) -> Int in
return result + next
}) // 10 => number1.reduce(0,+)도 가능
let number5: [Int] = number1.reduce(into: 0, { (result: inout Int, next: Int) in
result = result + next // result += next
}) // 10 => number1.reduce(0) { $0 += next }
모나드
- 순서가 있는 연산을 처리할 때 자주 활용하는 디자인 패턴 (정확한 내용은 아님)
- 범주론의 모나드 개념을 차용한 정도의 의미를 가짐 -> 모나딕(monadic), 모나딕 타입, 모나딕 함수 등으로 표현
- 스위프트에서 가장 기본적이면서도 유용한 모나드 : 옵셔널
- 조건
- 타입을 인자로 받는 타입 (특정 타입의 값을 포장) - 제네릭으로 구현 가능
- 특정 타입의 값을 포장한 것을 반환하는 함수(메서드)가 존재
- 포장된 값을 변환하여 같은 형태로 포장하는 함수(메서드)가 존재
컨텍스트
- 콘텐츠를 담은 무언가
- ex) 물: 콘텐츠, 물컵: 컨텍스트
함수객체
- 맵(map)을 적용할 수 있는 컨테이너 타입
- ex) Array, Dictionary, Set 등 많은 컬렉션타입이 함수 객체임
모나드
- 닫힌 함수객체 : 함수객체 중에서 자신의 컨텍스트와 같은 컨텍스트의 형태로 맵핑할 수 있는 함수객체
- 모나드 = 닫힌 함수객체
- 컨텍스트에 포장된 값을 처리하여 포장된 값을 컨텍스트에 다시 반환하는 함수(맵)를 적용할 수있음, 플랫맵(flatMap) 메서드를 활용
func doubleEvenNumber(_ num: Int) -> Int? {
if num % 2 == 0 {
return num * 2
}
return nil
}
Optional(3).flatMap(doubleEvenNumber) // 3을 꺼내서 확인, 2의 배수가 아니므로 nil을 반환
Optional(4).flatMap(doubleEvenNumber) // 4를 꺼내서 확인, 2의 배수 이므로 2배를 한 8을 반환 (이때 8을 옵셔널로 감싸서 줌 - 입력이 옵셔널이므로)
- Optional 타입에 사용한 flatMap() 메서드를 Sequence 타입이 Optional 타입의 Element를 포장한 경우엔 compactMap()dmf 사용
- compactMap은 옵셔널을 벗기고 내부의 element값만 넘겨줌
let optNumber: [Int?] = [1,2,3,nil,nil,6]
let mapNumber = optNumber.map{ $0 } // 1,2,3,nil,nil,6 - [Int?]형태
let compactNumber = optNumber.compactMap{ $0 } // 1,2,3,6 - [Int]형태