[Swift] 옵셔널(Optionals)

728x90


옵셔널(Optionals)에 대해 알아 보겠습니다.

 

옵셔널

옵셔널은 스위프트의 특징 중 하나인 안전성(Safe)를 담당하는 기능입니다.
다른 언어에서 볼 수 없었던 옵셔널이란 무엇일까요?
단어 뜻 그대로 옵셔널은 선택적인,즉 값이 있을수도 있고 없을 수도 있다는 뜻을 나타냅니다.
이는 변수나 상수에 나타낼 수 있는데 상수나 변수에는 꼭 값이 있다는 보장이 없죠? 값이 없을땐 nil일 수도 있다는 뜻입니다.

 

옵셔널?

다시 말하자면, swift에선 nil을 사용하기 위해선 옵셔널을 사용해야만 합니다.

var name: String = "jinTonics"
name = nil  // 오류

위에 소스를 보시면 왜 nil을 넣는데 오류가 날까요?
답은 name이라는 변수가 옵셔널이 아니라서 그렇습니다.
옵셔널에서만 nil을 사용할 수 있다고 했죠?

var name: String? = "jinTonics"
print(name) // jinTonics

name = nil
print(name) // nil

옵셔널을 읽을 때 해당 변수 또는 상수에는 값이 없을 수 있다.라고 생각하면 가장 도움이 될 것 같습니다. 물음표를 붙여주는게 편하고 읽기도 쉬워 긴 표현을 사용하진 않지만 옵셔널의 명확한 표현도 알아 두시면 좋습니다.

 

물음표(?), Optional<타입> 둘다 사용 가능
var optionalType1: String?
var optionalType2: Optional<Int>

 

옵셔널의 정의

public enum Optional<Wrapped> : ExpressibleByNilLiteral {
    case none
    case some(Wrapped)

    print init(_ some: Wrapped)

    ...
}

옵셔널은 열거형으로 구현되었습니다!
살펴보면 nil일때는 none케이스가 될 것이고, 값이 있을때는 some케이스가 될 것입니다.

Objective-C와 Swift의 nil의 차이점
-> Swift 에서의 nil 은 Objective-C 에서의 nil 과 다르다.
nil of Swift : 값이 없다 in 모든타입 (Reference or Value Type)
nil of Objective-C : 값이 없다 in Reference Type

 

Objective-C에서 nil과 null의 차이
오브젝티브C에서 nil은 객체 참조에 사용되고 null은 기타 다른 포인터 자료형에 사용합니다.

//nil 사용
MyClass *obj = nil
 
// null 사용
int *ptr = NULL;

 

옵셔널 추출

열거형의 some 케이스로 꼭꼭 숨어있는 `옵셔널의 값을 옵셔널이 아닌 값`으로 추출하는 방법에 대해 알아 보겠습니다.

 

강제 추출(Forced Unwrapping)

옵셔널의 값을 강제 추출하려면 옵셔널 값의 뒤에 느낌표(!)를 붙여주면 값을 강제로 추출하여 반환해줍니다.
옵셔널의 값을 추출하는 가장 간단한 방법이지만 가장 위험한 방법입니다.
왜냐하면 런타임 오류가 일어날 가능성이 가장 높기 때문입니다.

var name:String? = "jinTonics"
var yourName:String = name!

name = nil
yourName = name! // 런타임시 오류가 발생한다.

강제 추출방식 사용은 런타임 오류의 가능성이 내포되어있기 때문에 지양해야됩니다.

 

옵셔널 바인딩(Optional Binding)

다른 언어에서 위의 코드처럼 NULL값을 체크하는 방식과 비슷합니다.
스위프트에선 이런 방식을 새로 정한것을 옵셔널 방식이라고 합니다.

 

기본 문법

if name != nil {
    print(name)
}else{
    print("name은 nil")
}
var name: String? = "jinTonics"

//옵셔널 바인딩을 이용해 임시로 상수 할당을 하여 값을 넣어 줄 수 있다.
if let findName = name {
    print("My name is \(findName)")
} else {
    print("name은 nil입니다.")
}

//My name is jinTonics

if var findName = name {
    fineName = "haha"
    print("My name is \(findName)")
}else{
    print("name은 nil입니다.")
}

//My name is haha

옵셔널 바인딩은 옵셔널에 값이 있는지 확인할 때 사용합니다. 옵셔널에 값이 있다면 옵셔널에서 추출된 값을 일정 블록 안에서 사용할 수 있는 상수나 변수로 할당해서 옵셔널이 아닌 형태로 사용할 수 있도록 해줍니다.

 

예제에서 if문에 임시 상수와 변수로 할당했는데 if문 안에서만 사용할 수 있고, 밖에서나 else안에서도 사용할 수 없습니다.

옵셔널 바인딩을 통해 쉽표(,)를 이용해 한번에 여러 옵셔널의 값을 추출할 수도 있습니다.

var name: String? = "jinTonics"
var yourName: String? = nil

if let findName1 = name, let findName2 = yourName {
    print("여러 옵셔널을 추출 할 수 있습니다, \(findName1),\(findName2)")
}

 

암시적 추출 옵셔널(Implicitly Unwrapped Optionals)

때때로 nil을 할당하고 싶지만, 옵셔널 바인딩으로 매번 값을 추출하기 귀찮거나 로직상으로 nil 때문에 런타임 오류가 발생하지 않을 것 같다는 확신이 들때 nil을 할당해줄 수 있는 옵셔널이 아닌 변수나 상수가 있으면 좋을 겁니다. 이때 사용하는 것이 암시적 추출 옵셔널입니다.

 

옵셔널을 표시하고자 할 때 타입 뒤에 물음표(?)를 사용했지만, 암시적 추출 옵셔널을 사용하려면 타입 뒤에 느낌표(!)를 사용해주시면 됩니다.

암시적 추출 옵셔널로 지정된 타입은 일반 값처럼 사용할 수 있으나, 여전히 옵셔널이기 때문에 nil도 할당해 줄 수 있습니다.

그러나 nil이 할당되어 있을 때 접근을 시도하면 런타임 오류가 발생합니다.

var name: String! = "jinTonics"

print(name) // jinTonics

name = nil

//암시적 추출 옵셔널도 옵셔널이므로 당연히 바인딩 사용 가능
if var myName = name {
    print("My name is \(myName)")
} else {
    print("myName은 nil입니다")    
}

옵셔널을 사용할 때는 강제 추출 또는 암시적 강제 옵셔널을 사용하기보다는 옵셔널 바인딩을 이용하는것이 훨씬 안전하고 지향하는 방법입니다.

nil 병합 연산자

nil병합 연산자는 옵셔널을 사용할 때 아주 유용하게 사용될 수 있는 연산자 입니다.

  • A ?? B
    -> A가 nill이 아니면 A를 반환하고, A가 nil이면 B를 반환합니다.
let someValue: Int = optionalInt != nil ? optionalInt! : 0

// 위의 코드와 같은 결과를 볼 수 있지만 훨씬 더 안저하게 옵셔널을 다룰 수 있습니다.
let someValue: Int = optionalInt ?? 0