objective C에 비해 swift의 생소한것들 중에 optional value란것이 있다.
자바의 Nullable과 Nonnull과는 좀 마이 다르다.
어떤 변수(값이나 참조모두)에 대해 nil을 허용할것인가에 대한 옵션이 되겠는데 이게 무슨소용인가 싶을 것이다.
기존에 코딩시 습관적으로 if (someobject != nil) 을 삽입하던 코드를 좀더 간결하고 버그가능성을 줄일 수 있도록 많은 고민을 한듯 하다.
먼저 변수를 선언하는 방법은 그냥 선언하는것과 Optional로 선언하는 방법이 있겠다.
var normalValue: String = "samse"
var optionalValue: String?
optional의 의미는 nil을 허용하는가에 대한 것이다 따라서 아래 코드는 에러다.
normalValue = nil // Type 'String' does not conform to protocol 'NilLiteralConvertible'
아래코드는 정상코드이다.
optionalValue = nil
optional 변수는 Explicit optional 변수와 Implicit optional 변수가 있다.
?를 붙인것이 Explicit이고 !를 붙인것이 implicit이다.
var explicitValue : String?
var implicitValue : String!
Explicit변수는 참조 시 nil인지 확인을 하기 위해 Forced unwrapping을 해야 한다. 해당 변수에 !를 붙임으로서 forced unwrapping이 수행된다
.
var unwrappedExplicitValue = optionalValue!
반드시 forced unwrapper를 통해서 값을 추출해서 사용해야 에러가 발생하지 않는다.
if unwrapperExplicitValue != nil {
println( unwrapperExplicitValue)
}
Implicit 변수는 참조시 unwrapper를 안해도 되나 런타임에 오류가 발생 가능하므로 항상 주의해야 한다.
if implicitValue != nil {
println( implicitValue )
}
Optional binding을 통해서 코드를 좀더 간결하게 할 수 있다.
if let value = explicitValue { // explicitValue! 로 표기하지 않는다.
println("Value = \(value)")
}
형식은 아래와 같다.
if let [새변수명] = [optional 변수 또는 optional변수를 반환하는 함수] { //
..
}
위에서 [새변수명]은 scope이 if문 내 이다.
if문내에서 [새변수명]을 변경하고자 할 경우에는 let이 아니라 var를 사용한다.
if var value = explicitValue {
value = "changed to SAMSE"
println("Value = \(value)")
}
class Person {
var contact: Contact?
init() {}
}
class Contact {
var address: String?
var tel: String?
var email: String?
var extra: String = "admin@gmail.com"
init() {}
}
위 와 같이 클래스를 정의하고 아래처럼 Persion객체를 생성하였다.
var p = Person()
클래스 선언부에서 변수선언시 초기화하지 않았기 때문에 초기값은 extra를 제외하고 optional인 변수들은 모두 nil이 된다.
따라서 아래 코드는 p의 contact가 nil이기 때문에 컴파일 에러가 발생한다.
var email = p.contact!.email
nil 체크를 반드시 해주어야 한다.
if let contact = p.contact {
if let email = contact.email! {
println(email)
}
else {
println("nil email")
}
}
else {
priintln("nil contact")
}
이 때 위 코드를 간결하게 줄여주는 방법이 Optional Chaining 표기법이다. 이는 변수 뒤에 ?를 붙임으로서 가능하다.
다음 코드는 정상이다.
var email2 = p.contact?.email?
이 코드는 p.contact가 nil이므로 검사를 통해 email2에 nil을 대입한다. optional binding과 함께하여 다음과 같이 코드 작성이 가능하다.
if let email = p.contact?.email? {
println('email = \(email)")
} else {
println("email is nil")
}
email is nil이 표시될것이다.
값을 대입할 때도 chaining을 통해 nil이 반환되면 해당 변수에 값이 대입되지 않는다.
p.contact?.email? = "someone@gmail.com"
if let email = p.contact?.email? {
println("Email : \(email)")
} else {
println("Email is nil yet")
}
마찬가지로 Email is nil yet이 표시될것이다.
아래와 같이 contact를 유효하게 만들어주면 대입이 될것이다.
p.contact = Contact()
p.contact?.email? = "someone@gmail.com"
if let email = p.contact?.email? {
println("Email : \(email)")
} else {
println("Email is nil yet")
}
Email : someone@gmail.com 이 표시될것이다.
Contact클래스의 extra field는 optional 변수가 아니다.
class Contact {
var address: String?
var tel: String?
var email: String?
var extra: String = "admin@gmail.com"
init() {}
}
let extra = p.contact?.extra! // p.contact?extra가 String?을 반환함으로써 타입캐스팅 에러 발생
이는 다음과 같이 수정되어야 한다.
let extra = (p.contact?.extra)!
또는
if let extra = p.contact?.extra {
// ...
}
애플개발자가 고민하여 만든것 같은데 nil에 대한 대비를 코딩레벨로 끌어내려서 런타임에러가 많이 줄어들것으로 예상된다. 실제 개발하면서 빠르게 익숙해져야 할듯 하다.