iOS2015. 4. 3. 15:16



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)")

}


값이 nil인 경우 디폴트값을 사용하고자 할 수 있는데 이 때 ?? 연산자를 제공한다.


var value = explicitValue ?? "SAMSE"

위 코든는 explicitValue가 평가한바 nil이면 value에 "SAMSE"를 대입한다.



클래스도 참조로서 당연히 optional로 선언가능하고 사용가능한데 class내의 optional 변수를 참조하는데 좀더 간결하게 접근하는 방법을 제공한다. 애플개발자들이 많은 고민을 하면서 어떻게든 코드양을 줄이고 편하게 코딩하고자 한 노력이 보이는 부분이다.

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() {}

}


이는 optional chaing으로 접근 시 타입은 String형식이더라도 옵셔널인 String?으로 받게됨을 알아야 한다. <-- 중요 중요!!!

let extra = p.contact?.extra! // p.contact?extra String? 반환함으로써 타입캐스팅 에러 발생


이는 다음과 같이 수정되어야 한다.


let extra = (p.contact?.extra)!

또는

if let extra = p.contact?.extra {

    // ...

}

애플개발자가 고민하여 만든것 같은데 nil에 대한 대비를 코딩레벨로 끌어내려서 런타임에러가 많이 줄어들것으로 예상된다. 실제 개발하면서 빠르게 익숙해져야 할듯 하다.





Posted by 삼스