Swift) Optional이란 무엇인지 설명하시오.

옵셔널(Optional)이란?

  • 값이 있을수도 있고, 없을수도 있음을 표현
1
2
3
4
5
let optionalConstant : Int? = nil

// 옵셔널이 아닌 상수에 nil값을 할당하려고 하면
// 컴파일 오류 발생
// let someConstant : Int = nil
  • nil할당 될 수 있는지 없는지 표현
1
2
3
4
5
6
7
8
9
10
11
12
// someOptionalParam에는 nil이 할당 될 수 있다.
func someFunction(someOptionalParam:Int?) {
// ...
}

// someParam에는 nil이 할당 될 수 없다.
func someFunction(someParam:Int) {
// ...
}

someFunction(someOptionalParam:nil)
// someFunction(someParam:nil)

옵셔널을 사용하는 이유

  • 명시적 표현

    1. nil의 가능성을 코드만으로 표현 가능
    2. 문서/주석 작성 시간 절약
  • 안전한 사용

    1. 전달받은 값이 옵셔널이 아니라면 nil체크를 하지 않고 사용가능
    2. 예외 상황을 최소화하는 안전한 코딩
    3. 효율적인 코딩

옵셔널 문법과 선언

  • 옵셔널 문법

    • enum + generics
  • 옵셔널 선언

1
2
3
4
5
6
7
8
enum Optional<Wrapped> : ExpressibleByNiliteral {
case none // 옵셔널에 값이 없다.
case some(Wrapped) // 옵셔널 내외에 값이 있다.
}

// 옵셔널의 타입
let optionalValue : Optional<Int> = nil // 완전한 문법
let optionalValue : Int? = nil // 이렇게도 사용 가능

💡 물음표는 띄어쓰지 않는다!

옵셔널 표현

! (Implicitly Unwrapped Optional)

  • 암시적 추출 옵셔널
  • 기존 변수처럼 사용 가능
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
var optionalValue : Int! = 100

switch optionalValue {
case .none :
print("This Optional variable is nil")
case .some(let value) :
print("Value is \(value)")
}
// ----- 출력값
// Value is 100

// 기존 변수처럼 사용 가능
optionalValue = optionalValue + 1
print(optionalValue!)
// ----- 출력값
// 101

// nil 할당 가능
optionalValue = nil
print(optionalValue)
// ----- 출력값
// nil

// optionalValue에 nil을 할당해놓은 상태에서 + 1을 해주면
// 잘못된 접근으로 인한 런타임 오류 발생
// optionalValue = optionalValue + 1

? (General Optional)

  • 기존 변수처럼 사용 불가
  • 옵셔널과 일반 값은 다른 타입이므로 연산불가
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
var optionalValue : Int? = 100

switch optionalValue {
case .none :
print("This Optional variable is nil")
case .some(let value) :
print("Value is \(value)")
}
// ----- 출력값
// Value is 100

// nil 할당 가능
optionalValue = nil
print(optionalValue)
// ----- 출력값
// nil

// 기존 변수처럼 사용불가 - 옵셔널과 일반 값은 다른 타입이므로 연산불가
// optionalValue = optionalValue + 1

옵셔널 추출

  • 옵셔널에 들어있는 값을 사용하기 위해 꺼내오는 것이다.

옵셔널 바인딩

  • nil 체크 + 안전한 추출
  • 옵셔널 안에 값이 있는지 확인하고, 값이 있으면 값을 꺼내온다.
  • if-let 방식을 사용한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
func printName(_ name : String) {
print(name)
}

var myName : String? = nil

// printName은 기본 타입 그리고 myName은 옵셔널 타입으로
// 전달되는 값의 타입이 다르기 때문에 컴파일 오류발생
// printName(myName)

if let name : String = myName {
printName(name)
}else {
print("myName == nil")
}
// ----- 출력값
// myName == nil

// name 상수는 if-let 구문 내에서만 사용가능하다.
// 상수 사용범위를 벗어났기 때문에 컴파일 오류 발생
// printName(name)

var yourName : String! = nil

if let name : String = yourName {
print(name)
}else {
print("yourName == nil")
}
// ----- 출력값
// yourName == nil

// 쉼표(,)를 사용하여 한 번에 여러 옵셔널을 바인딩 할 수 있다.
// 모든 옵셔널에 값이 있을 때만 동작한다.
myName = "sujeong"
yourName = nil

// yourName이 nil이기 때문에 실행되지 않는다.
// if let name = myName, let friend = yourName {
// print("\(name) and \(friend)")
// }

yourName = "wonseok"

if let name = myName, let boyFriend = yourName {
print("\(name) and \(boyFriend)")
}
// ----- 출력값
// sujeong and wonseok

강제추출

  • 옵셔널에 값이 들어있는지 아닌지 확인하지 않고, 강제로 값을 꺼내는 방식이다.
  • 만약 값이 없을경우(nil) 런타임 오류가 발생하기 때문에 추천하지 않는다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func printName(_ name: String) {
print(name)
}

// 옵셔널의 값을 강제로 꺼내와서 전달하고자 한다면,
// myName의 옵셔널 물음표가 벗겨지면서 안에 있던 값 sujeong이 강제로 추출되어 (myName!)에 들어온다.
// 그러므로 옵셔널 타입이 아닌 스트링 타입 printName으로 값을 넘겨줄 수 있는 것이다.
var myName : String? = "sujeong"
var yourName : String! = nil

printName(myName!)
// ----- 출력값
// sujeong

myName = nil

// 강제추출시 값이 없으므로 런타임 오류 발생
// print(myName!)

yourName = nil

// nil 값이 전달되기 때문에 런타임 오류 발생
// printName(yourName)

옵셔널 체이닝

  • 옵셔널의 내부의 내부의 내부로 옵셔널이 연결되어 있을 때 유용하게 활용할 수 있다.
  • 매번 nil 확인을 하지 않고 최종적으로 원하는 값이 있는지 없는지 확인할 수 있다.

예제

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
// 사람 클래스
class Person {
var name : String
var job : String?
var home : Apartment?

init(name : String) {
self.name = name
}
}

// 사람이 사는 집 클래스
class Apartment {
var buildingNumber : String
var roomNumber : String
var `guard` : Person?
var owner : Person?

init(dong : String, ho : String) {
buildingNumber = dong
roomNumber = ho
}
}

// 옵셔널 체이닝 사용
let sujeong : Person? = Person(name:"sujeong")
let apart : Apartment? = Apartment(dong:"307",ho:"1203")
let superman : Person? = Person(name:"superman")

// 옵셔널 체이닝 실행 후 결과값이 nil일 수 있으므로
// 결과 타입도 옵셔널이다.

만약 우리집 경비원 직업이 궁금하다면?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// 옵셔널 체이닝을 사용하지 않는 경우
func guardJob(owner: Person?) {
if let owner = owner {
if let home = owner.home {
if let `guard` = home.guard {
if let guardJob = `guard`.job {
print("우리집 경비원의 직업은 \(guardJob)입니다.")
} else {
print("우리집 경비원은 직업이 없어요.")
}
}
}
}
}

guardJob(owner: sujeong)

// 옵셔널 체이닝을 사용하는 경우
func guardJobWithOptionalChaining(owner: Person?) {
if let guardJob = owner?.home?.guard?.job {
print("우리집 경비원의 직업은 \(guardJob)입니다.")
} else {
print("우리집 경비원은 직업이 없어요.")
}
}

guardJobWithOptionalChaining(owner: sujeong)
// ----- 출력값
// 우리집 경비원은 직업이 없어요.

nil 병합 연산자

  • ??
  • 중위 연산자
  • Optional ?? Value
  • 옵셔널 값이 nil 일 경우, 우측 값을 반환한다.
  • 띄어쓰기에 주의
1
2
3
4
5
6
7
8
9
10
11
12
13
var guardJob : String

guardJob = sujeong?.home?.guard?.job ?? "슈퍼맨"
print(guardJob)
// ----- 출력값
// 경비원

sujeong?.home?.guard?.job = nil

guardJob = sujeong?.home?.guard?.job ?? "슈퍼맨"
print(guardJob)
// ----- 출력값
// 슈퍼맨

정리

  • 옵셔널은 값이 있을수도, 없을수도 있음을 표현한다.
  • 옵셔널이 아닌 상수에 nil값을 할당하려고 하면 컴파일 오류가 발생한다.
  • 옵셔널은 열거형(enum)과 general의 합작품이라고 볼 수 있다.
  • !(Implicitly Unwrapped Optional)기존 변수처럼 사용이 가능하다.
  • ?(General Optional)은 옵셔널과 일반 값은 다른 타입이므로 기존 변수처럼 사용이 불가하다.
  • 옵셔널 바인딩은 옵셔널 안에 값이 있는지 확인하고, 값이 있으면 값을 꺼내오며 if-let 방식을 사용한다.
  • 옵셔널 바인딩 시 선언한 상수는 if-let 구문 내에서만 사용할 수 있으며, 상수의 사용 범위를 벗어나면 컴파일 오류가 발생한다.
  • 쉽표(,)를 통해 한번에 여러 옵셔널을 바인딩 할 수 있는데, 모든 옵셔널에 값이 있어야만 동작한다.
  • 강제추출은 옵셔널에 값이 들어있는지 아닌지 확인하지 않고, 강제로 값을 꺼내는 방식이다.
  • 강제추출은 옵셔널로 선언되어 있는 값을 강제로 추출한 후 옵셔널 타입이 아닌 다른 타입으로 값을 넘겨줄 수 있다.
  • 옵셔널 체이닝은 옵셔널의 내부의 내부의 내부로 옵셔널이 연결되어 있을 때 유용하며, 최종적으로 원하는 값이 있는지 없는지를 확인한다.
  • 옵셔널 체이닝 시, if-let 안에 들어있는 값들을 순서대로 확인하고 그 중 하나라도 값이 없다면, 멈춘 후 else문을 출력한다.
  • nil 병합 연산자는 ?? 이렇게 표현하며, 옵셔널 값이 nil 일 경우 우측 값을 반환한다.

참고

Swift) Optional이란 무엇인지 설명하시오.

https://suzumsz.github.io/2021/03/23/Swift/Optional/

Author

Sujeong Kim

Posted on

2021-03-23

Updated on

2021-10-05

Licensed under

댓글