Swift 옵셔널(Optional)
Swift를 공부하면서 매번 제대로 정리가 안되는 개념이 있는데 바로 옵셔널(Optional)이다.
그래서 이참에 포스팅하면서 정리해두려고 한다.
옵셔널이란 변수의 값이 있을 수도 있고 없을 수도 있다는걸 나타낸다.
불편해보이는 Optional 왜 사용할까 싶은데
Objective-C에 아직 nil타입이 존재하며, 프로젝트에서 obj-c와 swift를 혼용해서 사용할 수 있어 obj-c와 상호운용성을 위해 사용한다고 한다.
옵셔널이 필요한 이유 -> nil의 가능성을 명시적으로 표현
Type casting, nil value 체크 등에 있어서 중요한 역할
- nil 가능성을 문서화 하지 않아도 코드만으로 충분히 표현가능
- 문서 / 주석 작성 시간을 절약
- 전달 받은 값이 옵셔널이 아니라면 nil체크를 하지 않더라도 안심하고 사용
- 효율적인 코딩
- 예외 상황을 최소화하는 안전한 코딩
옵셔널이 아닌 상수에다가 nil값을 할당 시 컴파일 오류 발생
옵셔널은 열거형과 제너럴의 합작품. 아래와 같이 선언이 되어 있다.
enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
}
let optionalValue: Optional<Int> = nil
let optionalValue: Int? = nil
var optionalValue: Optional<Int> = 100 (완전한 문법)
var optionalValue: Int? = 100 (간략한 표현)
! - Implicitly Unwrapped (암시적 추출 옵셔널)
var optionalValue: Int! = 100
// 기존 변수처럼 사용 가능
optionalValue = optionalValue + 1
// nil 할당 가능
optionalValue = nil
// 잘못된 접근으로 인한 런타임 오류 발생, optionalValue에 nil이 들어가 있기 때문!
optionalValue = optionalValue + 1
? - 일반적인 옵셔널
var optionalValue: Int? = 100
// nil 할당 가능
optionalValue = nil
// 기존 변수처럼 사용불가 - 옵셔널과 일반 값은 다른 타입이므로 연산불가
optionalValue = optionalValue + 1
Optional Unwrapping, 옵셔널을 꺼내는 방법은 2가지 방법이 있다.
먼저, Optional Binding(옵셔널 바인딩)
nil체크 + 안전한 값 추출을 하는 방식으로, 옵셔널에 값이 있는 경우에만 값을 추출
if-let/var 방식, guard-let 방식을 이용한다.
// if-let 방식
func printName(_ name: String) {
print(name)
}
var myName: String? = nil
//printName(myName)
// 전달되는 값의 타입이 다르기 때문에 컴파일 오류발생
if let name: String = myName {
printName(name)
} else {
print("myName == nil")
}
// name 상수는 if-let 구문 내에서만 사용가능합니다
// 상수 사용범위를 벗어났기 때문에 컴파일 오류 발생
//printName(name)
힌번에 하나만 바인딩 가능한게 아닌 여러개 동시에도 가능합니다.
myName = "yagom"
yourName = nil
if let name = myName, let friend = yourName {
print("\(name) and \(friend)")
}
// yourName이 nil이기 때문에 실행되지 않습니다
yourName = "hana"
if let name = myName, let friend = yourName {
print("\(name) and \(friend)")
}
// yagom and hana
guard-let은 if-let과 비슷하지만, else인 부분만 작성이 가능하다.
값이 nil이여서 옵셔널이 추출되지 않은 경우만 어떠한 액션이 가능!
guard-let을 통과한 경우 저장된 상수는 전역변수로 사용 가능!!
guard-let의 else문 안에는 항상 return 아니면 throw문이 와야 한다.
// guard-let 방식
func optbinding(){
guard let name_m: String = myName else {
return print("myName is Optional")
}
print(name_m)
guard let name_y: String = yourName else {
return print("yourName is Optional")
}
print(name_y)
}
optbinding()
Force Unwrapping (옵셔널 강제 추출)
옵셔널 변수명 + !, 그다지 추천되는 방식은 아니다
printName(myName!) // yagom
myName = nil
// print(myName!)
// 강제추출시 값이 없으므로 런타임 오류 발생
var yourName: String! = nil
//printName(yourName)
// nil 값이 전달되기 때문에 런타임 오류발생