지금껏 배웠던 다른 프로그래밍 언어들과 달리 Swift가 가지는 특색이라고도 생각하는 Optional에 대해 간략하게 정리해볼 것이다.
Optional의 구조
Optional은 값이 있을 수도 있고, 없을 수 있는 타입이다. 그 내부 구조를 한 번 살펴보자.
@frozen enum Optional<Wrapped> {
case none
case some(Wrapped)
}
@frozen : enum에 더 이상 case를 추가하지 않겠다는 것을 의미
<Wrapped> : Generic 타입이다. wrapped이라는 건 말그대로 감싸져있다는 것이기에 이후 사용시 풀어서 사용해줘야 한다.
enum 구조이기 때문에 ?를 써서 사용하는 방법과는 다르게 표현할 수 있다.
// 값이 있는 경우
let optionalValue: Optional<Int> = Optional.some(100)
// nil
let optionalValue: Optional<Int> = Optional.none
하지만 역시... 아래 방법이 보기에도 편하고 사용하기도 편하다.
let optionalValue: Int? = 10
let optionalValue: Int? = nil
값이 없을 때는 nil로 반환되지만, 값이 있는 경우는 Wrap되어 있기 때문에 이를 풀어주는 방법들에 대해 알아보자.
Optional Unwrapping 방법 4가지
1. Foreced Unwrapping
이름 그대로 "강제로" 풀어주는 것을 말한다.
let optionalValue: Int? = 10
// ! 를 붙여서 강제 언래핑해준다.
print(optionalValue!) // 10
이렇게 쉽게만 풀 수 있었으면, 매번 !만 붙여서 언래핑했을 것이다. 하지만 값이 nil일 경우 어떻게 될까?
let optionalValue: Int? = nil
// ! 를 붙여서 강제 언래핑해준다.
print(optionalValue!) // Runtime Error
nil인 경우는 nil을 반환하지 않고 에러를 반환한다!
그렇기에 강제 언래핑의 경우, 값이 확실히 있다고 보장할 수 있는 상황에만 쓰이는 방식으로 지양해야한다.
2. Optional Binding
언래핑할 때 가장 자주 쓰이는, 지향하는 방식이다. 주로 if let이나 guard let을 통해 값이 있는지 확인하여, 그 값을 새로운 변수에 할당하여 사용하는 것이다. 바로 봐보자!
let optionalValue: Int? = 10
// guard
guard let val = optionalValue else {
print("값 없음")
}
print(val) // 10
// if
if let val = optionalValue {
print("값 : \(val)") // 10
} else {
print("값 없음")
}
값이 있는지 먼저 확인하고, 있다면 변수에 할당해주는 방식으로! 언래핑하는 방식 중에 가장 안전한 방식이다!
3. Optional Chaining
optional chaining이란 말 그대로 "chaining = 연쇄", 연쇄적으로 옵셔널을 사용하는 것이다.
struct Player {
var name: String
var position: String
}
var son: Player? = nil
// ?를 써서 접근
son?.name() // nil
// 값이 있다면
var park: Player? = Player(name: "Park", position: "MF")
park?.name // "Park"
체이닝의 경우 연쇄적으로 가능하기 때문에 유의할 점이 몇 가지 있다.
예시를 들어보자.
struct Player {
var name: String
var position: String
}
struct Team {
var teamColor: String
var player: Player
init(name: String, position: String, teamColor: String) {
self.teamColor = teamColor
player = Player(name: name, position: position)
}
}
var tottenham: Team? = Team(name: "son", position: "FW", teamColor: "White")
Team안에 Player가 속해있는 구조이고, Team을 옵셔널 타입으로 하는 tottenham이라는 변수를 만들어줬다.
이 변수 안의 player의 포지션을 알고 싶다면 어떻게 접근해야 할까?
let pos = tottenham?.player.position
똑같이 ?를 붙여서 사용해주면 된다.
추가적으로 pos의 타입은 체이닝의 가장 마지막인 position의 타입이 된다. 즉 Optional<String> 타입이 되는 것이다.
(+) 마지막 체이닝 요소가 옵셔널일 경우 ?를 생략해도 된다고 한다!
(+) 중간에 nil이 반환된다면 이후 체이닝 요소들은 모두 무시하고 종료한다.
코딩테스트를 볼 때 딕셔너리 자료형에 ?를 많이 붙혔던 걸 기억할 수 있을 것이다...!
딕셔너리 같은 경우도, value에 접근하기 위해서는 체이닝을 필요로한다!
let price: [String:Int] = ["PS4":200,000]
price["PS4"]?.count // 1
이런 식으로 value에 접근하려면 옵셔널 체이닝을 해줘야 한다!
4. Nil Coalescing
A ?? B 형태로 사용이 된다. "A가 nil이라면 B를 반환해라, 값을 가지고 있다면 A를 반환해라" 라는 의미이다.
특정 변수에 대해 대체할 값을 넣고자 한다면 이 방법이 좋을 것이다.
var optionalValue: Int? = nil
var replaceNil: Int = 100
// A ?? B, A와 B가 동일한 타입이어야 함
let realValue = optionalValue ?? replaceNil
print(realValue) // 100
언래핑하는 방식들에 대해서는 어느 정도 알고 있었지만, 다시 정리하는데 도움이 되었다.
새로 알게되었던 것은 Optional은 enum(열거형)으로 이루어져 있고, 값이 있는 경우는 Wrapper를 통해 감싸져 있다는 것!
끄읕