Open longlivedrgn opened 1 month ago
값이 없음
let number: Int? = Optional.some(42)
옵셔널 Unwrapping을 하는 방법
옵셔널 binding
강제 언래핑
optional 타입
이다.@IBOutlet weak var secondLabel: UILabel!
lazy와의 차이점?
??
을 활용하여 nil이면 ??
뒤에 있는 값을 던지게 한다.if let
/ while let
: 옵셔널에 값이 있는지 확인하고 값이 있다면 상수/변수에 할당해 해당 클로저 내부에서 사용할 수 있도록 한다.guard let
: guard문으로 옵셔널의 값을 확인해 값이 있다면 새로운 상수에 넣고 사용한다. 만약 nil이라면 else 뒤에 오는 클로저를 실행시킨다.switch
: 스위치문을 사용해 값이 있는 경우와 nil인 경우 코드를 작성한다.!
: 값이 있다면 언래핑을 통해 값을 사용할 수 있지만, nil인 경우 런타임 에러를 발생시킨다.let assumedString: String! = "hihi"
// 암시적 언래핑 옵셔널
let implicitString: String = assumedString
let optionalString = assumedString
print(type(of: assumedString)) // Optional<String>
print(type(of: implicitString)) // String
print(type(of: optionalString)) // Optional<String>
a ?? b
: 옵셔널 a에 값이 있다면 그대로 a를 언래핑해 반환하고 nil이라면 b를 반환한다.기본값을 설정할 때 주로 사용한다.
let text: String? = "안녕하세요"
// text에 값이 없다면 default value를 반환한다.
let title: String = text ?? "반갑습니다."
옵셔널은 .none, .some(Value) 두 케이스로 된 '(제네릭) 연관값을 갖는 열거형'이다.
@frozen enum Optional<Wrapped> {
case none
case some(Wrapped)
}
옵셔널 값을 추출한다는 것은 이 연관값을 추출해낸다는 뜻이고, 옵셔널 바인딩이란 이 추출해낸 (존재가 보장되어 접근해도 안전한) 값을 메모리 공간(변수)에 할당해 줌으로써 그 값을 안전하게 사용할 수 있도록 하는 것이다. 강제 언래핑이란 이 옵셔널 객체가 .none(연관값이 없는 케이스)인지 .some(연관값이 있는 케이스)인지를 체크하지 않고 연관값을 꺼내려 시도하는 것인데, 이때 .some(Value)이면 상관 없지만 .none이면 에러가 발생한다. 이러한 관점에서 봤을 때, 값이 없는데 꺼내려 하기 때문이라기보다는 깔 수 없는 객체를 까려고 해서라는 표현이 조금 더 정확한 것 같다.
옵셔널 체이닝은 '값'이 존재하지 않을 때 사용할 다른 값을 제시하는 것이다. 개인적으로는 이때 '다른 값'을 Value라기보다 .some(Value)로 이 해하면 '병합'이라는 표현이 직관적으로 와닿는 것 같다. 이를 switch-case문으로 표현하면 다음과 같다.
// a ?? b ?? c ?? ...
func ?? (lhs: Value?, rhs: Value?) -> Value? {
var result: Value? = nil
switch lhs {
case .some(let value): result = value
default:
switch rhs {
case .some(let value): result = value
case .none: result = nil
}
}
return result
}
옵셔널 체이닝은 guard let 구문으로 표현해 보면 아래와 같다.
// a?.b?.c?. ...
guard let a else {
return nil // return a
}
guard let b = a.b else {
return nil // return a.b
}
guard let c = b.c else {
return nil // return b.c
}
암시적 언래핑은 강제 언래핑처럼 '!
'를 쓰지만 전혀 다르다. 호출부가 아니라 정의(선언)부에서 !
를 쓸 때 암시적 언래핑을 뜻한다. 타입 자체는 옵셔널이지만 당연히 연관값 추출이 가능할 것이라는 전제를 깔아 주므로 호출부에서 언래핑을 해줄 필요가 없어진다. 편리하지만 위험하기에 접근 시 값이 있을 게 보장되는 경우에만 신중히 사용해야 한다. 주로 마주하는 용례는 XCTest의 관용적 네이밍 sut, UIKit의 IBOutlet들이다.
이에 앞서 옵셔널이 뭔지 정의가 되면 좋을 것 같습니다. 옵셔널은 대충 아래와 같은 열거형인데요.
enum Optional<Wrapped>: ExpressibleByNilLiteral {
case some(_ v: Wrapped>
case none
}
값이 있을 수도 있고, 없을 수도 있는 박스를 연상하면 좋습니다.
옵셔널 바인딩은 그 이름처럼 some에 있는 값이 연결이 되도록 해주는 절차인데, if let
, guard let
과 같은 문법으로 값이 있는지를 확인한 뒤 있다면 값을 꺼내게 됩니다.
강제 언래핑은 그 이름처럼 이 박스가 some인지 none인지를 확인하지 않고, 무조건 some이라고 가정하고 !
를 뒤에 붙여서 값을 꺼내는 절차인데요. none이면 값을 꺼내려고 해도 꺼낼 수가 없기 때문에 크래시가 납니다.
옵셔널인 프로퍼티에 대해 ?
를 뒤에 붙여서 값이 있다면 뒤로 이어지고, 아니라면 그대로 맥락이 종료가 되는 문법입니다.
human?.computer?.mouse?.click()
요런 맥락으로요. 사람이 없을 수도 있고, 컴퓨터가 없을 수도 있고, 마우스가 없을 수도 있습니다. 없으면 해당 맥락은 바로 종료가 되고 있으면 뒤의 맥락으로 이어집니다.
휴먼에러가 곧잘 일어나서 생각보다 잘 사용되지는 않는데요. 절차상 반드시 값이 존재하는 경우에 사용이 됩니다. 한편으로는 절차상 어떤 시점부터 값이 존재하는 경우가 있는데, 개발자가 인지하지 못하고 해당 시점 이전에 작업을 하려는 경우 크래시를 내어, 어떻게든 강제로 결을 맞추도록 유도하는 측면도 있습니다.
이러한 맥락으로 iOS 개발하면서 가장 쉽게 마주하는 값이 UIViewController의 view입니다.
값이 없다면 기본값을 주면서 박스를 없애버리는 작업입니다. 예를 들면 아래와 같은 맥락입니다.
let 먹을거 = 냉장고.냉동실?.2층?.삼겹살 ?? 라면
먹을거.먹기()
삼겹살 먹고 싶네요