let greensky: Person? = Person(name: "greensky")
let apart: Apartment? = Apartment(dong: 101, ho: 702)
let kim: Person? = Person(name: "kim")
Optional?
옵셔널은 변수의 값의 nil 일수 있다는 것을 표현합니다.
그말인즉슨... 변수가 가변형일때 값이 부재할 수 있다는것을 표현하는 것이죠.
반대로 논-옵셔널의 경우에는 변수가 nil일 수 없다는것을 표현하겠죠?
따로 옵셔널 표현을 쓰지 않았다면, 기본값은 논-옵셔널입니다.
nil에 대한 예외처리를 강제해 틈만나면 나타나서 괴롭히는 null 에러를
사전에 예방할 수 있다는게 큰 장점인거 같습니다.
예를 들면, swift는 키보드입력값에 바인딩되는 변수는 무조건 옵셔널 변수로 처리합니다.
입력값이 있을수도, 없을수도 있기 때문이죠!
Optional 선언
옵셔널 변수의 선언은 ?를 사용합니다
var input: String?
var input2: String
//옵셔널 선언이 없다면, 기본값은 논-옵셔널입니다.
input = nil //정상적으로 작동
input2 = nil //컴파일 에러
Optional 활용
아래 코드는 에러가 날까요 나지 않을까요?
var num1: Int? = 1
var num2: Int = 1
num1 + num2
네. 가차없이 바로 오류가 납니다.
옵셔널의 존재의의를 생각하면 당연히 오류가 나야합니다.
옵셔널 변수인 num이 만약 후에 nil값이 되었다면 당연히 연산에 오류가 날테니까요.
따라서, 옵셔널 변수로 연산을 하기 위해서는 unwrapping 또는 binding을 해야합니다.
1. Optional Binding (if-let)
var num1: Int? = 1
var num2: Int = 1
if let num1Binding: Int = num1 {
let sum = num1Binding + num2
print(sum) // 2
}
단, num1은 if-let구문 외에서는 사용하면 컴파일 오류가 발생합니다.
또한 num1Binding도 if-let 구문에서만 정의되어서 외부에서는 사용이 불가능합니다.
let-it 구문 안에서만 옵셔널 예외처리를 한 값을 쓰기때문에
비교적 안전하게 사용할 수 있습니다.
2. Optional Unwrapping
var num1: Int? = 1
var num2: Int = 1
print(num1! + num2)
var num1 = nil
print(num1! + num2) // error
//implicitly unwrapped optionals
var num1: Int! = nil
var num2: Int = 1
print(num1! + num2) // error
자칫 남용하다간 수많은 nil 에러를 맞이할수도 있는 방법입니다.
이는 인스턴스의 모든값이 다 세팅되있는 클래스의 초기화처럼,
nil값이 아님이 명확한 상황에서 사용해야 합니다.
3. Optional Chaining
만약, 옵셔널끼리의 연산을 하게된다면 어떨까요?
객체마다 옵셔널 바인딩을 해야하니 스파게티 코드가 쓰여질게 벌써 예상이 되죠?
이럴때 Optional Chaining을 사용한다면 Optional 예외처리를 간단하게 할 수 있습니다.
Optional Chaining은 .를 통해 클래스의 프로퍼티에 접근해서 Optional 예외처리를 합니다.
class Person {
var name: String
var job: String?
var home: Apartment?
init(name: String){
self.name = name
}
}
class Apartment {
var owner: Person?
var dong: Int
var ho: Int
var roomMate: Person?
init(dong: Int, ho: Int){
self.dong = dong
self.ho = ho
}
}
Person과 Apartment class를 정의했습니다.
Person 클래스에서 name은 이니셜라이저를 통해 최초 인스턴스 생성시 무조건 존재햐아하는 프로퍼티입니다.
그리고 사람의 직업과 집은 있을수도, 없을수도 있는 옵셔널 값으로 job과 home을 설정했습니다.
Apartment 클래스에서는 동, 수는 이니셜라이저로 입력되는 필수 프로퍼티 이구요,
해당 호수에 주인, 주인과 같이사는 룸메이트가 있을수도, 없을수도 있는 값으로
owner와 roomMate는 옵셔널입니다.
이어서 인스턴스를 생성하고 프로퍼티를 입력하겠습니다.
let greensky: Person? = Person(name: "greensky")
let apart: Apartment? = Apartment(dong: 101, ho: 702)
let kim: Person? = Person(name: "kim")
func roomMatJobFind(owner: Person?){
if let roomMateJob = owner?.home?.roomMate?.job {
print("우리집 룸메이트의 직업은 \(roomMateJob)입니다" )
} else{
print("우리집 룸메이트는 백수입니다" )
}
}
roomMatJobFind(owner: greensky) //우리집 룸메이트는 백수입니다
저, 엄청 해멧습니다....
kim의 job이야 설정하지 않았으니 백수로 나오는건 당연한데
dump로 greensky와 apart를 출력해보면 greensky의 home, apart의 owner 및 roomMate 전부
nil로 출력되는데, 이게 연산이 어떻게 if let roomMateJob = owner?.home?.roomMate?.job가
정상적으로 실행이되서 kim의 직업 nil값을 뽑아네 else 구문을 돌린거지?? 라고 생각을 했습니다..
어떻게 이해했는지 적으면 글이 너무 길어지니까 접은글로 작성하겠습니다.
네, 백수라고 출력되는게 당연했습니다. 왜냐구요?
func hoomRoomMateFind(owner: Person?){
if let hoomRoomMate = owner?.home?.roomMate?.name {
print("우리집 룸메이트의 직업은 \(hoomRoomMate)입니다" )
} else{
print("룸메이트 없어요" )
}
}
func hoomFind(owner: Person?){
if let home = owner?.home?.dong {
print("우리집 룸메이트의 직업은 \(home)입니다" )
} else{
print("집도없어요" )
}
}
hoomRoomMateFind(owner: greensky) //룸메이트 없어요
hoomFind(owner: greensky) //집도없어요
kim의 job이 nil이여서 roomMatJobFind의 else구문이 출력된게 아니라,
애초에 roomMate와 home도 nil이여서 출력된 것이니까요.
이거를 두어시간 끙끙대다니...ㅠㅠ 더욱더 정진하도록 하겠습니다..
자, 이어서 입력을 더 해봅시다.
greensky?.home = apart
roomMatJobFind(owner: greensky) //우리집 룸메이트는 백수입니다
greensky?.home?.roomMate = kim
roomMatJobFind(owner: greensky) //우리집 룸메이트는 백수입니다
greensky?.home?.roomMate?.job = "programmer"
roomMatJobFind(owner: greensky) //우리집 룸메이트의 직업은 programmer입니다
저는 좀 이렇게 단계별로 진행하니까 겨우 이해를 했네요... 옵셔널 체이닝 힘든 공부였습니다..ㅠㅠ
글을 다 작성하고 보니 roomMatJobFind의 else문의 출력은
옵셔널 체이닝 도중 nil값이 존재하여 nil값이 출력되었습니다. 라고 했어야됬나 봐요...ㅠ
근데 만약 중간중간 프로퍼티의 nil출력에 대한 모든 반응을 하려면 옵셔널 체이닝을 사용하지 못합니다.
func guardJob(owner: Person?) {
if let owner = owner {
if let home = owner.home {
if let roomMate = home.roomMate {
if let roomMateJob = roomMate.job {
print("룸메이트의 직업은 \(guardJob)입니다")
} else {
print("룸메이트는 백수예요")
}
}
else{
print("룸메이트가 없어요")
}
}
else{
print("집이없어요")
}
}
}
guardJob(owner: greensky)
이렇게 써야하거든요....ㅎ
nil 병합 연산자
??를 중위연산자로 사용합니다.
var roomMateJob : String
roomMateJob = greensky?.home?.roomMate?.job ?? "백수"
print(roomMateJob) //programmer
greensky?.home?.roomMate?.job = nil
roomMateJob = greensky?.home?.roomMate?.job ?? "백수"
print(roomMateJob) //백수
감이 오시나요? 옵셔널 값이 nil이라면 우측의 값을 반환합니다.
도움을 준 사이트들
전반적인 공부 가이드라인
옵셔널에 대한 전반적인 정리
http://monibu1548.github.io/2018/05/12/swift-optional/
옵셔널에 대한 자세한 설명 및 예시
https://zeddios.tistory.com/16
'Language > Swift' 카테고리의 다른 글
Swift 사용자 정의 타입 - Class (0) | 2021.03.11 |
---|---|
Swift 사용자 정의 타입 - Struct (0) | 2021.03.11 |
Swift 반복문 (0) | 2021.03.10 |
Swift 다양한 switch-case 패턴들 (0) | 2021.03.09 |
Swift의 조건문 (0) | 2021.03.09 |