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에서 해제
- 생성(initialization)
Retain
- 객체에 대한 참조횟수를 증가, 참조 횟수는 객체에 대한 활성화된 참조의 수를 나타내며,참조횟수가 1이상인 경우 객체는 메모리에 유지
- 예 : 객체를 변수에 할당,다른 객체에 대한 속성으로 사용하면 해당 객체에 참조 횟수가 증가
Release
- 객체에 대한 참조 횟수를 감소 시키는 작업
- 참조횟수가 0이되면 객체는 memory에서 해제
Stored Property
- class , enum, struct 에서 실제 값을 저장하는 변수, 상수
Computed Property
- 계산된 프로퍼티는 실제 값을 저장하지 않고 특정 코드 블록에서 계산된 결과를 반환하는 프로퍼티
- get : 프로퍼티 값을 읽을 떄 호출
- set : 프로퍼티에 값을 할당할 떄 호출 다른 프로퍼티 값을 기반으로 계산, 매번 호출될 떄마다 결과가 다름
728x90
'기타 > Today I Learned' 카테고리의 다른 글
[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 |