오늘은 Optional에 대해서 정리해보자.
이전에도 정리했지만 재밌는게 있어서 다시 한 번 정리하고 넘어간다.
공식 문서를 확인해 보자.
A type that represents either a wrapped value or
nil
, the absence of a value.
값 혹은 nil
을 감싸고 있는 타입이며 nil
은 값의 부재를 의미한다.
그럼 이렇게 정리할 수 있을거 같다.
Optional은 값이 있을 수도 없을 수도 있는 경우를 표현하는 타입이다.
Optional은 enum
과 Generics
로 이뤄져 있다.
이 부분은 아래에서 다시 살펴보도록 하고 지금은 enum
과 Generics
로 구현되어 있다는 것만 알고 넘어가자.
Optional을 사용해보자.
let longForm: Optional<Int> = Int("42")
let shortForm: Int? = Int("42")
longForm
을 보면 Optional<Int>
로 타입을 정의하고 있다.
Optional은 Generics
로 구현되어 있으므로 Optional의 Generics
에 Int
라는 타입을 사용하겠다고 알려주고 있다.
하지만 타입을 정의하는 부분이 길고, 안 이쁘다.🤔
Swift type 시스템은 Optional<Int>
처럼 전체 type을 표시하는 대신 사용할 type 뒤에 ?
을 사용해 Optional type을 정의하도록 지원한다.
Swift type 시스템의 지원으로 shortForm
의 type 정의가 가능해졌다.
훨씬 읽기 쉽고 짧아졌다.👏
enum Optional<Wrapped> {
case none
case some(Wrapped)
}
Optional은 none
, some
case로 구성된 enum
이다.
.none
은 nil
과 동일하다.
.some
은 래핑된 값을 저장한다.
let number: Int? = Optional.some(42)
let noNumber: Int? = Optional.none
print(noNumber == nil)
number
에는 Optional.som(42)
로 42를 래핑해 저장할거라고 알려준다.
noNumber
는 Optional.none
으로 nil
을 저장한다.
Optional은 값이 있을 수도 없을 수도 있는 타입이다.
Swift는 해당 타입을 안전하게 사용하기 위해 다양한 unWrapping 방법을 제공하고 있다.
Optional 인스턴스의 래핑된 값을 새로운 값으로 unWrapping하기 위해 if let
, guard let
, switch
를 사용할 수 있다.
let number: Int? = 5
if let bindingNumber = number {
print("This is binding number: \(bindingNumber)")
} else {
print("This is nil")
}
guard let bindingNumber = number else {
print("This is nil")
return
}
print("This is binding number: \(bindingNumber)")
switch number {
case .none:
print("This is nil")
case .some(let bindingNumber):
print("This is binding number: \(bindingNumber)")
}
Optional은 enum으로 구현되어 있으므로 .none
, .some
case를 switch문으로 분기할 수 있다.
unWrapping된 property와 method에 안전하게 접근하기 위해 Optional Chaining 연산자 ?
을 사용할 수 있다.
let str: String? = "abcd"
if str?.hasPrefix("a") == true {
print("yes")
} else {
print("no")
}
let str2: String? = nil
if str2?.hasPrefix("a") == true {
print("yes")
} else {
print("no")
}
if str2?.hasPrefix("a") == nil {
print("nil")
}
Optional Chaining은 래핑된 값을 그대로 사용하고 chain에 nil
이 존재한다면 nil
을 반환한다.
str?.hasPrefix()
를 보면 ?
로 Optional Chaining이 사용된 것을 알 수 있다.
🤔그렇다면 str2처럼 nil이라면 어떤 결과가 나올까?
if문을 통해 확인해보자.
정답은
nil
이 출력된다.
Optional Chaining은 래핑된 값을 그대로 사용하므로 str2
는 nil
을 가지고 있다.
chain에 nil
이 존재하므로 결과로 nil
이 반환된다.
??
연산자를 사용하면 Optional type에 기본값을 지정해줄 수 있다.
let number: Int? = nil
let basicNumber = number ?? 0
print(basicNumber)
number
는 nil
이므로 ??
연산자로 정해준 기본 값인 0이 basicNumber
로 지정된다.
Optional에 값이 있다고 확신할 수 있는 경우 !
연산자를 사용해 강제 언래핑 할 수 있다.
let number: Int? = 5
print(number!)
let numberNil: Int? = nil
print(numberNil!)
number
는 Optional이지만 값을 가지고 있다.
number
는 5라는 값을 가지고 있으므로 !
를 사용해 강제 언래핑 할 수 있다.
numberNil
은 nil
이다.
numberNil
을 강제 언래핑하면 어떻게 될까?
nil
을 강제 언래핑하면 런타임에 error를 마주할 수 있다.
❗️ 그러므로 꼭 값이 있다고 확신할 수 있는 경우에만 사용하자.
!
연산자를 통해 강제 언래핑을 알아봤다.
let number: Int! = 1
위에서 Int!
는 무엇일까?
강제 언래핑이라고 하기엔 이상하다.
사실 Int!
와 같은 형태는 iOS를 공부하면서 아래와 같은 형태로 많이 접한다.
@IBOutlet weak var button: UIButton!
Int?
, Int!
에 대해 알아보자.
Int?
는 Optional type임을 우리는 알고 있다.
그렇다면 Int!
는 무엇일까?🤔
var valueOne: Int? = 1
var valueTwo: Int! = 1
valueOne = nil
valueTwo = nil
위의 코드를 실행해보면 error 없이 실행된다.
즉, Int?
, Int!
모두 nil
을 할당할 수 있다.
🤔 흠... 그런데 위와 같이 String!
타입인 str
을 선언하고 nil
을 할당했다.
그리고 str
method를 사용하려고 했는데 error가 나왔다.
unexpectedly found nil while implicitly unwrapping an optional value
"암시적 언래핑에서 예기치 못하게 nil
이 발견되었다"라고 한다.
Int!
와 같은 형태를 암시적 언래핑이라고 한다.
암시적 언래핑 타입은 nil을 지정할 수는 있지만 nil일때 접근하면 위와 같은 error를 받는다.
@IBOutlet weak var button: UIButton!
위의 코드는 iOS 프로젝트를 진행하다보면 정말 많이 보게되는 코드이다.
weak
에 대해서도 할 말이 많지만 다음으로 미뤄두고!
UIButton!
에 대해 생각해보자.
우선, Swift의 클래스는 안전을 위해 프로퍼티가 세팅되기 전까지는 사용할 수 없다.
이는 클래스를 초기화할때 프로퍼티에 초기 값이 없으면 초기화가 불가능해 클래스의 프로퍼티에 nil이 입력된다.
즉, !
를 붙여두면 nil 상태지만 run되면서 스토리보드와 연결 되는 것이다.
(!
은 nil값을 지정할 수 있으므로)
음.. 나도 아직 빡!!하고 느낌은 안 오지만 내가 이해한 바로는 다음과 같다.
그리고 처음에는 nil
였다 viewDidLoad 이후부터 값이 변하므로 var
와 함께 선언되어야 한다.
(틀린 부분이 있다면 알려주면 너무너무 고마울거 같다🙇)
오늘은 Optional에 대해 알아봤다.
Optional 코드가 궁금해서 이어서 code를 뜯어보려고 한다.
궁금하다면 아래 링크로...!
그럼 이만👋
UIButton!과 같은 forced unwrapping optional은 스토리보드와 연결될 때 nil이 아닌 값이 되지 않습니다.
뷰컨트롤러가 viewDidLoad가 되면 뷰 객체들이 생성되면서 nil이 아니게 됩니다.
참고 : http://minsone.github.io/mac/ios/iboutlet-forced-unwrapping-vs-optional