개발하는 뚝딱이

[swift] 옵셔널 체이닝과 빠른 종료 본문

swift

[swift] 옵셔널 체이닝과 빠른 종료

개발자뚝딱이 2020. 5. 8. 13:18

 

책 <스위프트 프로그래밍 3판 ;야곰 지음> 을 정리한 글입니다.

 


 

 

1. 옵셔널 체이닝

- Optional Chaining

- 옵셔널에 속해 있는 nil일 수도 있는 프로퍼티, 메서드, 서브스크립션 등을 가져오거나 호출할 때 사용할 수 있는 일련의 과정

- 옵셔널 값이 있으면 프로퍼티, 메서드, 서브스크립트 등을 호출

- 옵셔널이 nil이면 프로퍼티, 메서드, 서브스크립트 등은 nil을 반환

- 즉, 옵셔널을 반복 사용하여 옵셔널이 자전거 체인처럼 서로 꼬리를 물고 있는 모양이기 때문에 옵셔널 체이닝이라고 부름

 

 

- 사람의 주소 정보 표현 설계

class Room { // 호실
    var number: Int // 호실 번호
    
    init(number: Int) {
        self.number = number
    }
}

class Building { // 건물
    var name: String // 건물 이름
    var room: Room? // 호실 정보
    
    init(name: String) {
        self.name = name
    }
}

struct Address { // 주소
    var province: String // 광역시/도
    var city: String // 시/군/구
    var street: String // 도로명
    var building: Building?
    var detailAddress: String? // 건물 외 상세주소
}

class Person { // 사람
    var name: String // 이름
    var address: Address? // 주소
    
    init(name: String) {
        self.name = name
    }
}

 

- Eric 인스턴스와 옵셔널 체이닝

let eric: Person = Person(name: "Eric")
let ericRoomViaOptionalChaining: Int? = eric.address?.building?.room?.number // nil
let ericRoomViaOptionalUnwrapping: Int = eric.address!.building!.room!.number // 오류 발생

 

- 옵셔널 바인딩과 옵셔널 체이닝 비교

// 옵셔널 바인딩

let eric: Person = Person(name: "Eric")
var roomNumber: Int? = nil
if let ericAddress = eric.address {
    if let ericBuilding = ericAddress.building {
        if let ericRoom = ericBuilding.room {
            roomNumber = ericRoom.number
        }
    }
}

if let number: Int = roomNumber {
    print(number)
} else {
    print("Can not find room number")
}
// 옵셔널 체이닝

let eric: Person = Person(name: "Eric")
if let roomNumber: Int = eric.address?.building?.room?.number {
    print(roomNumber)
} else {
    print("Can not find the room number")
}

 

- 옵셔널 체이닝을 통한 값 할당

- 상위 프로퍼티의 값이 없으면 nil을 반환하며, 체이닝이 도중에 중지됨

eric.address?.building?.room?.number = 505
print(eric.address?.building?.room?.number) // nil

eric.address = Address(province: "서울시", city: "중구", street: "을지로", building: nil, detailAddress: nil)
eric.address?.building = Building(name: "빌딩1")
eric.address?.building?.room = Room(number: 0)
eric.address?.building?.room?.number = 505

print(eric.address?.building?.room?.number) // Optional(505)

 

- 옵셔널 체이닝을 통한 서브스크립트 호출

- 옵셔널의 서브스크립트를 사용할 때, 프로퍼티 이름과 대괄호([) 사이에 물음표(?)를 표기해줘야 함

let optionalArray: [Int]? = [1, 2, 3]
optionalArray?[1] // 2

var optionalDictionary: [String: [Int]]? = [String: [Int]]()
optionalDictionary?["numberArray"] = optionalArray
optionalDictionary?["numberArray"]?[2]

 

 

2. 빠른 종료

- Early Exit

- guard 구문을 이용하며, if 구문과 유사하게 Bool 타입의 값으로 동작

- guard 뒤에 따라붙는 코드의 실행 결과가 true일 때 코드가 계속 실행됨

- guard 구문 뒤에는 항상 else 구문이 뒤에 따라와야 하며, 꼭 자신보다 상위의 코드 블록을 종료하는 코드가 들어가야 함

- 그래서 특정 조건에 부합하지 않다는 판단이 되면 재빠르게 코드 블록을 종료할 수 있음

- 현재의 코드 블록을 종료할 때는 return, break, continue, throw 등의 제어문 전환 명령 사용

 - fatalError()와 같은 비반환 함수나 메서드를 호출할 수 있음

더보기

비반환함수

- 종료되지 않는 함수, 정상적으로 끝나지 않는 함수

- 비반환 함수가 실행되면 프로세스가 죽어버림

- 재정의는 가능하지만 비반환 타입은 변경할 수 없음

- 리턴 타입은 Never

 

- if 구문과 guard 구문의 비교

// if 구문을 사용한 코드
for i in 0...3 {
    if i == 2 {
        print(i)
    } else {
        continue
    }
}

// guard 구문을 사용한 코드
for i in 0...3 {
    guard i == 2 else {
        continue
    }
    print(i)
}

 

- guard 구문의 옵셔널 바인딩 활용

func greet(_ person: [String: String]) {
    guard let name: String = person["name"] else { return }
    print("Hello \(name)!")
    
    guard let location: String = person["location"] else {
        print("I hope the weather is nice near you")
        return
    }
    
    print("I hope the weather is nice in \(location)")
}

var personInfo: [String: String] = [String: String]()
personInfo["name"] = "Jenny"

greet(personInfo) // Hello Jenny!
// I hope the weather is nice near you

personInfo["location"] = "Korea"
greet(personInfo) // Hello Jenny
// I hope the weather is nice in Korea

 

- 조금 더 구체적인 조건을 추가하고 싶다면 쉼표(,)로 추가조건을 나열해주면 됨

- 추가된 조건은 Bool 타입 값이어야 함

 - 또 쉼표(또는 &&)로 추가된 조건은 AND 논리연산과 같은 결과를 줌

func enterClub(name: String?, age: Int?) {
    guard let name: String = name, let age: Int = age, age > 19, name.isEmpty == false else {
        print("You are too young to enter the club")
        return
    }
    
    print("Welcome \(name)!")
}

 

 - guard 구문의 한계는 자신을 감싸는 코드 블록의 제어문 전환 명령어를 쓸 수 없는 상황이라면 사용이 불가능

- 함수나 메서드, 반복문 등 특정 블록 내부에 위치하지 않는다면 사용이 제한됨

let first: Int = 3
let second: Int = 5

guard first > second else {
    // 여기에 들어올 제어문 전환 명령이 딱히 없음
    // 오류!
}

'swift' 카테고리의 다른 글

[swift] 타입캐스팅  (0) 2020.05.17
[swift] 맵, 필터, 리듀스  (0) 2020.05.13
[swift] 클로저  (0) 2020.05.07
[swift] 접근제어  (0) 2020.05.02
[swift] 인스턴스 생성 및 소멸  (0) 2020.05.01