본문 바로가기
iOS/Swift

[Swift] Optional (2)

by Jiseong 2021. 8. 19.

2021.08.18 - [Dev/Swift & IOS] - [Swift] Optional (1)

 

[Swift] Optional (1)

Swift는 Optional이라는 중요한 개념을 갖고있다. 정말 중요하다. Optional은 Type Casting이나, nil value check 등 에 중요한 역할을 한다. Optional은 '?'을 변수나, 변수 타입에 붙여 표현한다 Optional의 '?..

limjs-dev.tistory.com

 

Optional이 아직 헷갈린다면 기본을 설명한 윗 글을 보고 오길

 

 

이전 글엔 Optional, nil을 설명하였다.

이번 글엔 Optional Bindind & chaining, nil 병합.. 

 

먼저 .. Wrapping 부터 알아보자

 

Wrapping

 

Optional 타입은 기본적으로 래핑되어있는 상태이다.

Wrapping은.. 음 무엇으로 감싸져있다고 생각하면된다. 우리가 실생활에서 쓰듯 무엇을 보존할 때 랩으로 감는 것 처럼 말이다.

 

Optional로 선언된 변수의 값은 그 값이 존재하는지..  nil 상태인지 래핑이 되어있어 모르는 상태인 것이다.

간단하게 생각하자면.. 래핑을 하여 컴파일시 nil체크를 함으로써 에러를 방지하고 수정할 수 있게해줌 - 장점임

 

let optionalValue: Int? = 1004
print(optionalValue) //Optional(1004)

 

위와 같이 래핑되어 출력된다.

 

근데 우리는 1004만 출력되길 원하지, Optional로 감싸져있는 값이 출력되길 원하지않는다.

그럴 때 사용하는 것이 이제 '!' (exclamation mark) 이다.

 

Forced Unwarpping

 

강제 언래핑은 "옵셔널로 변수 선언을 했지만, 이 변수엔 무조건 값이 있어" 하고 보장이 된 경우 사용하는 것이다.

 

변수명 뒤에 '!'을 붙이면 Optional을 강제로 벗긴다. 예제를 보자

 

let optionalValue: Int? = 1004
print(optionalValue!) //1004

 

부가 예제

 

let optionalValue: String? = nil
print(optionalValue) //nil

print(optionalValue!) //error

이렇게 값이 없는데 보호막을 벗기면 에러가 난다.

 

그러므로 nil 체크를 항시 하자.

let optionalValue: String? = nil

if optionalValue != nil {
	print(optionalValue)
}

 

 

Optional Binding

 

Optional Binding은 Optional 타입으로 선언된 변수에 값이 있는지 없는지 확인하는 기능이다. 

강제 언래핑을 하지않고 nil 체크를 할 수 있기에 더 안전한 형태로 값을 추출 할 수 있다.

 

기본 형태

if let 변수 = 옵셔널 변수 {
	//변수에 옵셔널 변수의 값이 할당됨
}

사용해보자

let optionalValue: String? = "천사"

if let myValue = optionalValue { //optionalValue에 값이 존재하면
    print("\(optionalValue)의 실체.. 두둥: \(myValue)")
} else { //optionalValue에 값이 없으면
    print("\(optionalValue)의 실체는 밝혀지지않음")
}
//출력: Optional("천사")의 실체.. 두둥: 천사

print(myValue) //error: cannot find 'myValue' in scope

위를 보자.

 

OptionalValue가 Optional 타입으로 선언되어 있고, 원래 이 OptionalValue 값을 출력하기 위해서는 !을 사용하여 언래핑해야한다.

하지만, Optional Binding은 먼저 이 OptionalValue의 값이 있는 경우와 없는 경우로 나누고,

값이 있는 경우, myValueOptionalValue의 실제값(언래핑 값)을 할당한다. 

또한 myValue는 if문 안에서만 할당되는 지역 변수이므로, if 밖에서는 myValue를 사용할 수 없다.

 

 

Optional Chaining

 

어.. 갑자기 이걸 뭐라 설명해야될지 모르겠다.

 

애플 공식문서에서 가져와보겠음

 

Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil. If the optional contains a value, the property, method, or subscript call succeeds; if the optional is nil, the property, method, or subscript call returns nil. Multiple queries can be chained together, and the entire chain fails gracefully if any link in the chain is nil.

 

https://docs.swift.org/swift-book/LanguageGuide/OptionalChaining.html

 

Optional Chaining — The Swift Programming Language (Swift 5.5)

Optional Chaining Optional chaining is a process for querying and calling properties, methods, and subscripts on an optional that might currently be nil. If the optional contains a value, the property, method, or subscript call succeeds; if the optional is

docs.swift.org

 

 

음..  마지막 문단을 보자. 여러개의 쿼리가 연결될 수 있고, 전체 연결 중 하나라도 nil이라면 실패한다..

여기서 실패는 nil을 반환한다는 것이겠지

 

예제로 알아보자.

 

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

let john = person()
let roomcount = john.residence?.numberOfRooms //nil

john의 거주지는 없다. 그러므로 nil 출력

하지만 옵셔널을 벗겨주면?

 

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}
let john = Person()
let roomcount = john.residence!.numberOfRooms //Fatal error: Unexpectedly found nil while unwrapping an Optional value

이렇게 에러가 난다. 언래핑하는데 nil값이 있다고 안된다는거.

Person 클래스의 residence 인스턴스는 Residence? 타입인데 john은 아직 residence 지정을 받지못했기때문에 값이 없는 것이다.

 

해결해보자

 

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}
let john = Person()

john.residence = Residence()

let roomcount = john.residence!.numberOfRooms //1 ---- Forced Unwrapping

여기까진 !을 써서 강제 언래핑을 사용한 것이다. 

강제 언래핑은 쉽게 에러가 발생할 수 있기에, 변수 내에 값이 무조건 있다고 보장될 때만 사용하도록 권장된다.

 

여기서 Optional Chaining을 사용한다면 더 안전하게 실행되도록 할 수 있다. (nil이 나오지않게)

 

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}
let john = Person()
john.residence = Residence() //residence 지정
//let roomcount = john.residence!.numberOfRooms

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
//출력: John's residence has 1 room(s).

 

위의 코드를 보면 강제 언래핑과 다르게 ?를 사용하는 것을 볼 수 있다.

이 때, if let roomCount = john.residence?.numberOfRooms 문은 해당 chain(john, residence?, numberOfRooms)에 값이 있는지 없는지를 체크하고, 위에서 명시했듯이 하나라도 없으면 조건문에 false를 반환한다.

 

 

 

nil 병합

 

간단하다. 

??은 중위 연산자이다.

var Eg = Optional ?? value //옵셔널 값이 nil일 경우, 우측의 값을 반환

 

예제

 

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}
let john = Person()
john.residence = Residence()

//let roomcount = john.residence!.numberOfRooms //Forced Upwrapping

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
var newRoomNumber = john.residence?.numberOfRooms ?? 3 //출력: 1

john.residence?.numberOfRooms에 1이란 값이 들어가있기때문에 좌측의 값을 반환한다.

 

그 반대도 해보자

 

class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}
let john = Person()
//john.residence = Residence() //집 무너짐 = nil

//let roomcount = john.residence!.numberOfRooms //Forced Upwrapping

if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}
var newRoomNumber = john.residence?.numberOfRooms ?? 3 //출력: 3

 

john.residence?.numberOfRooms의 값이 nil인 경우 우측의 값이 반환되어 3이 출력되는 모습을 볼 수 있다.

 

옵셔널 값이 nil일 경우, 우측의 값을 반환한다. 그리고 반드시 띄어쓰기에 주의하여야 한다.

 

'iOS > Swift' 카테고리의 다른 글

[Swift] String and Characters  (0) 2021.08.24
[Swift] API Design Guidelines  (0) 2021.08.24
[Swift] Optional (1)  (0) 2021.08.18
[Swift] Struct Mutating  (0) 2021.08.11
[Swift] Class & Structure  (0) 2021.08.11

댓글