[TIL] Swift 문법 옵셔널 체이닝(Optional Chaining)

2023. 5. 17. 02:38·Swift/문법
728x90

옵셔널 체이닝(Optional Chaining)

  • 언래핑 없이 옵셔널 값의 멤버에 접근
  • 현재 Nil일 수 있는 옵셔널인 프로퍼티, 메서드, 서브스크립트를 조회하고 호출하기 위한 프로세스
  • 옵셔널에 값이 포함되어 있으며 프로퍼티,메서드,서브스크립트는 호출에 성공
  • 옵셔널이 nil 이면 프로퍼티,메서드,서브스크립트 호출은 nil을 반환
  • 여러 조회는 함꼐 연결될 수 있고 체인에 어느 부분이라도 nil이면 전체 체인은 실패

강제 언래핑 대안으로 옵셔널 체이닝(Optional Chaining as an Alternative to Forced Unwrapping)

  • 프로퍼티,메서드,서브스크립트를 호출하려는 옵셔널 값 뒤에 물음표(?)를 배치하여 옵셔널 체이닝을 지정,값에 강제 언래핑을 하기 위해 옵셔널 값 뒤에 느낌표(!) 를 배치하는 것과 유사
  • 차이점
    • 옵셔널이 nil일 때 옵셔널 체이닝은 실패하는 반면에 강제 언래핑은 런타임 에러가 발생
  • 옵셔널 체이닝은 nil값에 대해 호출될 수 있다는 사실을 반영하기 위해 조회하는 프로퍼티,메서드,서브스크립트가 옵셔널 값이 아닌 값을 반환하더라도 항상 옵셔널 값으로 반환
  • 옵셔널 반환 값으로 옵셔널 체이닝 호출 성공(반환된 옵셔널 체이닝에 값이 포함됨)했는지 실패(반환된 옵셔널 값은 nil)했는지 확인 할 수 있음
  • 특히 옵셔널 체이닝 호출 결과는 예쌍되는 반환값과 동일한 타입이지만 옵셔널로 레핑됨.
    • 일반적으로 Int로 반환하는 프로퍼티는 옵셔널체이닝을 통해 접근하면 Int?를 반환
// 옵셔널 체이닝이 강제 언래핑과 어떻게 다른지 보여주고 성공 여부를 확인
// Residence 인스턴스는 기본값이 1 인 numberOfRooms 라는 Int 프로퍼티를 가지고 있음. ADAP 인스턴스는 Residence? 타입의 옵셔널 residence 프로퍼티를 가지고 있음
// 새로운 ADAP 인스턴스를 생성하면 residence 프로퍼티는 옵셔널 규칙에 따라 nil 로 초기화
class ADAP {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

// bulmang 은 nil 의 residence 프로퍼티 값을 가짐
let bulmang = ADAP()

// 값에 강제 언래핑을 하기 위해 residence 뒤에 느낌표를 배치하여 사람의 residence 에 numberOfRooms 프로퍼티를 접근하면 residence 값이 없기 때문에 런타임 에러가 발생
// let roomCount = bulmang.residence!.numberOfRooms

// 옵셔널 체이닝은 numberOfRooms 의 값에 접근하기 위한 대안으로 제공
// Swift가 옵셔널 residence 프로퍼티를 "체인"하고 residence 가 존재하면 numberOfRooms 값을 조회
if let roomCount = bulmang.residence?.numberOfRooms {
    print("Bulmang's residence has \\(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}

// nil 값을 갖지 않기 위해 bulmang.residence에 Residene 인스턴스를 할당할 수 있음.
// bulmang.residence 는 nil 이 아닌 실제 Residence 인스턴스를 포함
bulmang.residence = Residence()

if let roomCount = bulmang.residence?.numberOfRooms {
    print("Bulmang's residence has \\(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}

옵셔널 체이닝에 대한 모델 클래스 정의(Defining Model Classes for Optional Chaining)

  • 하나 이상의 레벨 깊이인 property,method,subscript 를 호출하기 위해 옵셔널 체이닝을 사용 할 수 있음
  • 타입 호환된 복잡한 모델 내에서 하위 property로 내려갈 수 있으며 해당 하위 property,method,subscript에 접근 가능한지 확인 할 수 있음
// 여러 레벨 옵셔널 체이닝의 예를 포함하여 몇몇의 후속 예제에서 사용할 4개의 모델 클래스를 정의

class Ada {
    var residence: Residence?
}

class Residence1 {
    var rooms: [Room] = []
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i:Int) -> Room {
        get {
            return rooms[i]
        }
        set {
            rooms[i] = newValue
        }
    }
    func printNumberOfRooms() {
        print("The numger of rooms is \\(printNumberOfRooms)")
    }
    // 옵셔널 프로퍼티
    var address: Address?
}

class Room {
    let name: String
    // 방 이름을 초기화 지정 초기화 구문
    init(name: String) { self.name = name }
}

class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if let buildingNumber = buildingNumber, let street = street {
            return "\\(buildingNumber) \\(street)"
        } else if buildingName != nil {
            return buildingName
        } else {
            return nil
        }
    }
}

옵셔널 체이닝을 통해 프로퍼티 접근(Accessing Properties Through Optional Chaining)

  • 옵셔널 값의 프로퍼티에 접근하고 프로퍼티 접근이 성공하면 검사하기 위해 옵셔널 체이닝을 사용할 수 있음
let malty = Ada()
if let roomCount = malty.residence1?.numberOfRooms {
    print("Malty's residence has \\(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}

// 옵셔널 체이닝을 통해 프로퍼티의 값을 설정
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "포항시 지곡로 철길"
// malty.residence?.address = someAddress 오류 발생 (이 할당은 = 연산자의 우항의 코드는 평가되지 않으므로 옵셔널 체이닝의 일부입니다. 이전 예제에서 상수에 접근하는 것은 어떠한 영향도 없기 때문에 someAddress 가 평가되지 않는다는 것을 쉽게 파악할 수 없음)

func createAddress() -> Address {
    print("Function was called.")

    let someAddress = Address()
    someAddress.buildingNumber = "29"
    someAddress.street = "Acacia Road"

    return someAddress
}
malty.residence1?.address = createAddress()

옵셔널 체이닝을 통한 함수 호출(Calling Methods Through Optional Chaining)

  • 옵셔널 값의 메서드를 호출하고 메서드 호출이 성공적인지 확인하기 위해 옵셔널 체이닝을 사용, 메서드가 반환값을 정의하지 않아도 사용 할 수 있음
func createAddress() -> Address {
    print("Function was called.")

    let someAddress = Address()
    someAddress.buildingNumber = "29"
    someAddress.street = "Acacia Road"

    return someAddress
}
malty.residence1?.address = createAddress()

// Void 의 암시적 반환 타입을 가지고 있습니다. 이것은 () 의 값 또는 빈 튜플을 반환한다는 의
//func printNumberOfRooms() {
//    print("The number of rooms is \\(numberOfRooms)")
//}

if malty.residence1?.printNumberOfRooms() != nil {
    print("It was possible to print the number of rooms.")
} else {
    print("It was not possible to print the number of rooms.")
}

옵셔널 체이닝을 통한 서브 스크립트 접근(Accessing Subsripts Through Optional Chaining)

  • 옵셔널 값의 서브 스크립트에서 값을 조회하고 설정하고 해당 서브스크립트 호출이 성공했는지 확인하기 위해 옵셔널 체이닝을 사용
// Residence 클래스에 정의된 서브 스크립트를 사용하여 john.residence 프로퍼티에 rooms 배열의 첫번째 방 이름을 조회
// 현재 malty.residence는 nil

if let firstRoomName = malty.residence1?[0].name {
    print("The first room name is \\(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}

// residence 가 현재 nil 이므로 서브 스크립트 설정은 실패
malty.residence1?[0] = Room(name: "Bathroom")

// malty.residence 에 실제 Residence 인스턴스를 생성하고 할당하면 옵셔널 체이닝으로 Residence 서브 스크립트를 사용하여 rooms 배열의 항목을 접근
let maltyHouse = Residence1()
maltyHouse.rooms.append(Room(name: "Living Room"))
maltyHouse.rooms.append(Room(name: "Kitchen"))
malty.residence1 = maltyHouse

if let firstRoomName = malty.residence1?[0].name {
    print("The first room name is \\(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}

옵셔널 타입에 서브 스크립트 접근(Accessing Subscripts of Optional Type)

  • 서브 스크립트가 Swift의 Dictionary 타입의 키 서브 스크립트와 같이 옵셔널 타입의 값을 반환하는 경우 옵셔널 반환값을 연결하기 위해 서브 스크립트의 닫는 대괄호 뒤에 물음표를 추가
var testScores = ["Bulmang": [86, 82, 84], "Jun": [79, 94, 81]]
testScores["Bulmang"]?[0] = 91
testScores["Jun"]?[0] += 1
testScores["Malty"]?[0] = 72

여러 수준의 체인연결 (Linking Multiple Levels of Chaining)

  • 여러 수준의 옵셔널 체이닝을 연결하여 모델 내에서 프로퍼티, 메서드, 그리고 서브 스크립로 깊게 접근할 수 있습니다. 그러나 여러 수준의 옵셔널 체이닝은 반환된 값에 더 많은 수준의 옵션성을 추가하지 않음
  • 조회하려는 타입이 옵셔널이 아니면 옵셔널 체이닝 때문에 옵셔널이 됨
    • 옵셔널 체이닝으로 Int 값을 조회하려고 하면 사용된 체이닝의 수준과 상관없이 항상 Int? 가 반환
  • 조회하려는 타입이 이미 옵셔널이면 체이닝 때문에 더 많은 옵셔널이 되지 않음
    • 유사하게 옵셔널 체이닝으로 Int? 값을 조회하려고 하면 사용된 체이닝의 수준과 상관없이 항상 Int? 가 반환
// malty 의 residence 프로퍼티에 address 프로퍼티에 street 프로퍼티를 접근
// residence 와 address 프로퍼티를 통해 연결하기 위해 2단계 수준의 옵셔널 체이이 사용되며 둘 다 옵셔널 타입
if let maltyStreet = malty.residence1?.address?.street {
    print("John's street name is \\(maltyStreet).")
} else {
    print("Unable to retrieve the address.")
}
// Prints "Unable to retrieve the address."

let maltysAddress = Address()
maltysAddress.buildingName = "포항"
maltysAddress.street = "지곡동 철길"
malty.residence1?.address = maltysAddress

// 옵셔널 체이닝을 통해 street 프로퍼티의 값에 접근
if let maltyStreet = malty.residence1?.address?.street {
    print("John's street name is \\(maltyStreet).")
} else {
    print("Unable to retrieve the address.")
}
// Prints "John's street name is Laurel Street."

옵셔널 반환값으로 메서드 체이닝 (Chaining on Methods with Optional Return Values)

모르는 용어

Escaping Closure

  • 함수 밖에서 실행되는 escaping closure
  • 예시 : 비동기로 실행되는 HTTP Request CompletionHandler보통 closure가 다른 변수에 저장되어 나주에 실행되게 함
  • 비동기로 실행될 때 escaping closure가 사용
  • escaping closure라고 선을 해도 non-escaping closure를 인자로 넣고 사용할 수 있다
    • 그렇다면 모든 closure가 ecaping을 선언해도 상관 없지 않을까?
    • non-escaping closure는 compiler가 클로저를 언제 종료될지 알기 때문에 클로저에서 사용하는 특정 객체에 대한 retain,release 등의 처리를 생략해 life-cycle을 효율적으로 관리

Life-Cycle

  • 상태의 변경을 감지하고 필요한 업데이트를 수행하여 화면에 최신상태를 유지
  • 생성(initialization) - 업데이트(update) - 레이아웃(layout) - 그리기(drawing) - 해제(deallocation)
    • 생성(initialization)
      • View 생성 및 초기화 코드가 실행되고 속성들이 설정
    • 업데이트(update)
      • view가 업데이트 되는 단계, 속성이나 상태변경을 감지하고 해당 view를 update
    • 레이아웃(layout)
      • view가 화면에서 배치되는 단계,view 계층의 크기와 위치를 계산하여 화면에 적절하게 배치
    • 그리기(drawing)
      • view가 화면에 그려지는 단계,view의 내용을 화면에 표시
    • 해제(deallocation)
      • view가 memory에서 해제되는 단계 viewrk 화면에서 제거되거나 더 이상 필요하지 않을 때 SwiftUI는 해당 view를 memory에서 해제

Retain

  • 객체에 대한 참조횟수를 증가, 참조 횟수는 객체에 대한 활성화된 참조의 수를 나타내며,참조횟수가 1이상인 경우 객체는 메모리에 유지
  • 예 : 객체를 변수에 할당,다른 객체에 대한 속성으로 사용하면 해당 객체에 참조 횟수가 증가

Release

  • 객체에 대한 참조 횟수를 감소 시키는 작업
  • 참조횟수가 0이되면 객체는 memory에서 해제

Stored Property

  • class , enum, struct 에서 실제 값을 저장하는 변수, 상수

Computed Property

  • 계산된 프로퍼티는 실제 값을 저장하지 않고 특정 코드 블록에서 계산된 결과를 반환하는 프로퍼티
    • get : 프로퍼티 값을 읽을 떄 호출
    • set : 프로퍼티에 값을 할당할 떄 호출 다른 프로퍼티 값을 기반으로 계산, 매번 호출될 떄마다 결과가 다름
728x90

'Swift > 문법' 카테고리의 다른 글

[TIL] Swift 문법 초기화(Initializer) - 2  (0) 2023.05.11
[TIL] Swift 문법 초기화(Initialization) - 1  (0) 2023.05.10
[TIL] Swift 문법 상속(Inheritance)  (0) 2023.05.05
[TIL] Swift 문법(Subscripts)  (0) 2023.05.03
[TIL] Swift 문법 메소드(Methods)  (0) 2023.04.28
'Swift/문법' 카테고리의 다른 글
  • [TIL] Swift 문법 초기화(Initializer) - 2
  • [TIL] Swift 문법 초기화(Initialization) - 1
  • [TIL] Swift 문법 상속(Inheritance)
  • [TIL] Swift 문법(Subscripts)
bulmang
bulmang
모바일 개발자 도전
  • bulmang
    bulmang
    bulmang
  • 전체
    오늘
    어제
    • 분류 전체보기 (208) N
      • 알고리즘 (68) N
        • List (3)
        • Two Pointer (6)
        • Binary Search (4)
        • Prefix Sum (3)
        • Sort (4)
        • Brute Force (5)
        • Array (2)
        • String (4)
        • 프로그래머스 (12)
        • 백준 (9)
        • Queue (2)
        • Stack (2)
        • Recursion (12) N
      • Computer Science (16)
        • Computer Architecture (6)
        • Operating System (5)
        • Network (2)
        • 기타 (2)
        • System Programming (1)
      • Swift (70)
        • 개발 (24)
        • 정리 (25)
        • 문법 (20)
      • Flutter (24)
      • 기타 (12)
        • 후기 (12)
      • Git (6)
      • Ios 오픈소스 (5)
      • UI 디자인 (5)
      • AppleScript (2)
  • 링크

    • Notion
    • Github
  • 태그

    Swift
    IOS
    SwiftUI
    재귀
    피플
    til
    FLUTTER
    협업
    자료구조
    Xcode
    컴퓨터구조
    백준
    문법
    today i learned
    Java
    코딩테스트
    알고리즘
    riverpod
    Apple Developer Academy
    개발
  • 최근 댓글

  • 최근 글

  • 인기 글

  • 250x250
  • hELLO· Designed By정상우.v4.10.2
bulmang
[TIL] Swift 문법 옵셔널 체이닝(Optional Chaining)
상단으로

티스토리툴바