옵셔널 (Optional)


옵셔널 (Optional) 은 랩핑이 되어있는 값과 존재하지 않는 값인 nil 중 하나를 나타내는 형태입니다.

간단하게 설명하면 이 변수에는 값이 있을수도 있고, 없을수도 있다는 표현입니다.

옵셔널 (Optional) 은 변수에 들어있는 값을 안전하게 꺼내 사용 할 수 있어 런타임 에러 (Runtime Error) 에 매우 안전한 방법입니다.


옵셔널 (Optional) 은 "?" 라는 특수문자로 표현합니다, 이 표현은 뒤에 설명될 옵셔널 체이닝 (Optional Chaining) 에도 사용됩니다.


기본적으로 표현하는 형태는 다음과 같습니다.


var optionalIntValue: Int?                 // Shorthand Form
var optionalIntValue: Optional<Int>        // Longhand Form



 위 코드와 같이 Shorthand Form 과 Longhand Form 이 있는데

글자수는 작지만 의미가 똑같이 축약되어있는 Shorthand Form 을 많이 사용합니다.






옵셔널 바인딩 (Optional Binding)


위에서 설명했듯이, 옵셔널 (Optional) 은 변수에 들어있는 값을 안전하게 꺼내 사용 할 수 있는 방법입니다.

그럼 이 옵셔널 변수를 어떻게 사용해야 안전하게 사용하는 것 일까요?


옵셔널 바인딩은 옵셔널 제어 구조 (Optional Control Structures) 를 사용하여 옵셔널 변수값을 꺼내 사용하는 형태를 말합니다.

옵셔널 제어 구조를 사용한 옵셔널 바인딩의 사용방법은 다음과 같습니다.


// if let
if let intValue = optionalIntValue {
    print(intValue)
}
 
// guard let
guard let intValue = optionalIntValue else {
    return
}
 
print(intValue)
 
// switch
switch optionalIntValue {
    case 1:
        print(optionalIntValue)
    default:
        print("default")
}



1. if let 

옵셔널 값인 optionalIntValue 의 값을 nil 체크 후에 nil 이 아닌 값이 있다면 intValue 변수에 값을 전달하고

그 안에서 intValue 변수를 컨트롤합니다.


2. guard let

옵셔널 값인 optionalIntValue 의 값을 nil 체크 후에 nil 이 아닌 값이 있다면

intValue 변수에 값을 전달하고 해당 코드를 통과하여 다음코드가 실행됩니다.

값이 nil 이라면 해당코드를 통과하지못하고 else 로 진입 후에 코드를 실행하고 return 에 의해 다음 코드와 상관없이 함수가 종료됩니다.


3. switch

옵셔널 값인 optionalIntValue 값을 미리 정해 놓은 값으로 필터링 할때 사용됩니다.

기본적으로 case 를 사용하여 원하는 값을 정해주고 일치할때 각각 실행될 코드를 구현합니다.

미리 정해놓지 않은 값이 나왔을땐 default 로 진입하여 코드를 실행합니다.


추가적으로 switch로 구분할 property가 enum 과 같이 명확하게 갯수가 정해져있다면

 default를 따로 정하지않고 enum에서 정해놓은 case 만 구현해도 에러가 나지 않습니다.






옵셔널 강제 언래핑 (Optional Forced Unwrapping)


옵셔널 값을 안전하게 사용하게 위해서는 위에 설명한 옵셔널 바인딩을 사용해야 합니다.

그 외에 확실하게 값이 있다는 가정하에 사용하는 옵셔널 강제 언래핑이 있습니다.


옵셔널 강제 언래핑은 "!" 라는 특수문자를 사용합니다.


기본적으로 표현하는 형태는 다음과 같습니다.


var optionalIntValue: Int!
 
optionalIntValue = 0
 
print(optionalIntValue)    // print 0



위 코드와 같이 "!" 를 사용하여 선언하고 print 로 출력하는건 중간에 0이라는 값을 optionalIntValue 에 넣어뒀기때문에 에러없이 동작합니다.

이렇게 있을수도 있고 없을수도 있는 애매한 옵셔널 값을 명확하게 드러내지만

옵셔널 바인딩 없이 강제로 언래핑하여 사용한다면 말 그대로 강제로 벗겨내기때문에

값이 nil 일 경우 런타임 에러가 발생하게 됩니다.


var optionalIntValue: Int!    // nil
 
print(optionalIntValue)        // Runtime Error
 
 
var optionalIntValue: Int?    // nil
 
print(optionalIntValue!)    // Runtime Error



위 코드와 같이 선언할때 강제 언래핑을 사용했거나 옵셔널 변수를 강제 언래핑 할경우에 런타임 에러가 생기는 경우가 굉장히 많으므로

옵셔널 바인딩을 사용해 값을 확인하고 언래핑하여 사용하는 방법이 제일 안전한 방법이라고 생각합니다.





nil 과 합쳐진 연산자 (Nil-Coalescing Operator)


옵셔널 값을 언래핑하여 사용할때 옵셔널 바인딩 (Optional Binding) 을 사용하기도하지만

nil 과 합쳐진 연산자 (Nil-Coalescing Operator) 를 사용하여 좀더 간단하게 사용하기도합니다.


nil 과 합쳐진 연산자는 "??" 라는 특수문자를 사용합니다.


var optionalIntValue: Int?    
let nonOptionalIntValue: Int = 0
 
print(optionalIntValue ?? nonOptionalIntValue)        



위 코드는 기본적으로 optionalIntValue 를 print 하지만 optionalIntValue 의 값이

nil 일경우 nonOptionalIntValue 의 값을 print 하는 방법입니다.

nil 과 합쳐진 연산자를 사용할땐 전자는 항상 옵셔널 타입 (Optional Type) 이어야 하고 후자는 전자와 타입 (Type) 이 같은 값이 있어야 합니다.

nil 과 합쳐진 연산자는 조건의 검사와 언래핑을 좀더 간결하고 읽기 쉽게 만들어 줍니다.


또한 ?? 연산자는 여러개를 이어서 사용할 수 있습니다.


var optionalIntValue: Int?
var optionalIntValueSecond: Int?
let nonOptionalIntValue: Int = 0
 
print(optionalIntValue ?? optionalIntValueSecond ?? nonOptionalIntValue)        




옵셔널 체이닝 (Optional Chaining)


옵셔널체이닝(Optional Chaining)은 값이 있을지도 모르는 Property, Method, Subscript를 쿼리를 통해 호출하는 Process입니다.

Optional 요소에 값이 들어있다면 PropertyMethodSubscript의 호출에 성공합니다.

Optional 요소에 값이 nil 이라면 PropertyMethodSubscript의 호출에 nil 이 반환됩니다.

여러개의 쿼리(Multiple query)를 함께 연결 할 수 있고, 여러개의 쿼리가 연결되있을때 한곳이라도 반환이 nil 일 경우 전체가 nil 로 반환됩니다.



Swift 에서의 옵셔널 체이닝(Optional Chaining) 은 Objective-C 의 메시징(Messaging) 과 비슷 하지만 

모든 유형에서 작동하고, 성공 과 실패 여부를 확인하는 방식으로 되어있습니다.






강제 언래핑을 대체한 옵셔널 체이닝 (Optional Chaining as an Alertnative to Forced Unwrapping)


Optional 요소의 값이 nil 이 아닐경우 호출하길 원하는 PropertyMethodSubscript 값 뒤에 ? (물음표, Optional Chaining) 를 붙여서

Optional Chaining을 지정합니다.

이와같은 형식은 Optional 요소의 값을 강제로 해제하기 위한 ! (느낌표, Forced Unwrapping) 과 비슷합니다.


? (물음표, Optional Chaining) 와 ! (느낌표, Forced Unwrapping) 의 가장 큰 차이점은


Optional Chaining을 사용하여 Unwrapping시에 Optional 값이 nil 이라면 에러가 아닌 정상적으로 nil 값을 반환합니다.

즉 항상 Optional에 대한 값이 있습니다

Forced Unwrapping을 사용하여 Unwrapping시에 Optional 값이 nil 이라면 정상적인 nil 반환이 아닌 런타임 에러를 반환하며 앱이 죽습니다. 


추가적으로 Optional Chaining을 사용하여 값을 호출했을때 결과값은 생각하고있는 타입과 같지만 Optional로 wrapping되어있습니다.

예를들어 Optional Chaining을 사용하여 접근했을때 일반적으로 생각하는 Int 타입이 아닌 Int? 타입을 반환합니다.


class Person {
    var residence: Residence?
}
 
class Residence {
    var numberOfRooms = 1
}


2개의 클래스를 정의한 예를들어 설명하자면 


Residence 인스턴스는 numberOfRooms 라는 Int Property를 가지고 있습니다.

Person 인스턴스는 Residence? 타입의 residence Property를 가지고 있습니다.


Person 인스턴스를 새로 생성했을때 residence Property는 Optional로 선언되어있으므로 기본적으로 nil 로 초기화됩니다.


let john = Person()



따라서 이 코드에서 선언된 john 안에 residence 프로퍼티는 nil 값을 가지게 됩니다.

이제 john.residence 에 대한 Optional 값을 추출해봅니다.


let roomCount = john.residence!.numberOfRooms


코드를 보면 john.residence 가 Optional이 아닌 값이었다면 이상없이 값이 반환되어 roomCount 변수에 저장됩니다.

하지만 john.residence 가 Optional 타입으로 선언되어있기때문에 이 코드는 런타임 에러가 발생합니다.


Optional Chaining은 이런 상황에서 값에 접근할 수 있게 합니다.

Optional Chaining을 사용하기 위해 ! (느낌표, Forced Unwrapping) 위치에 ? (물음표, Optional Chaining) 를 사용해봅니다.


if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
else {
    print("Unable to retrieve the number of rooms.")
}



Swift 에서 residence Optional Property Chain은 residence 가 존재한다면 numberOfRooms 의 값을 가져오라는걸 뜻합니다.

numberOfRooms 값에 접근하는건 실패할수도 있기 때문에 Optional Chaining은 Int? 값을 반환합니다.

예제를보면 residence 가 nil 일때 Int? 는 nil 이 되어 numberOfRooms 값에 접근할수 없기 떄문에

else 구문으로 빠져서 Unable to retrieve the number of rooms. 라는 print를 출력할것입니다.

residence 가 nil 이 아니라면 numberOfRooms 로 접근하여 반환될 Int? 라는 정수를 Unwrapping 하기 위해

옵셔널바인딩(Optional Binding)을 사용하여 roomCount 변수에 Int 값을 반환합니다.


또 하나의 중요한점은 numberOfRooms 의 값은 Int 지만 

Optional Chaining을 통해서 numberOfRooms 의 값을 받아온다면Int가 아닌 Int? 값을 반환한다는 부분입니다.





그럼 20000








'Swift' 카테고리의 다른 글

[Swift] typealias  (0) 2018.05.28
[Swift] VLC ( View Life Cycle )  (0) 2017.11.09
[Swift] ALC ( App Life Cycle )  (0) 2017.11.08
[Swift] Thread  (0) 2017.11.07
[Swift] GCD (Grand Central Dispatch)  (1) 2017.11.07

+ Recent posts