2023년 1월 1일
08:00 AM
Buffering ...

최근 글 👑

[Swift-Study] 기초 문법종합반 1주차 2일차 정리 - 옵셔널

2024. 3. 12. 00:41ㆍIOS/Swift-Study
SMALL

* 값이 없음을 포함하는 옵셔널의 개념 *

[ 옵셔널과 nil ]

 * 옵셔널(Optional) *

값이 없을 수 있는 상황에서 옵셔널(Optional)을 사용

옵셔널은 물음표 '?' 로 나타냄

(타입 어노테이션)

 

* 옵셔널의 두 가지 가능성 *

값이 있고 옵셔널로 래핑(wrapping)해놓은 값을

언래핑(unwrapping)하여 해당 값에 액세스

 

값이 전혀 없다.

옵셔널 타입끼리의 연산은 불가능

// 축약 타입 표현
var Code: Int? = 404 

// 정식 타입 표현
var myName: Optional<String> = "멍멍이"

func pay(with card: String?) {
   // 구현 코드
}

// 옵셔널 타입끼리의 연산은 불가능
var num1: Int? = 4
var num2: Int? = 2

num1 + num2 // 에러 발생!

let A: String? = "Hello, "
let B: String? = "world!"

// 옵셔널 String 값들을 연결하려는 시도
let result = A + B // 에러 발생!

 

변수에 nil을 할당함으로써 값이 없는 상태의 옵셔널 프로퍼티를 만들 수 있습니다.

var A: Int? = 404
A = nil // A에 옵셔널 Int가 들어가져 있는데 변수 A에 nil을 넣을 수 있습니다.
// var로 선언이 되어서 재할당이 가능해져 nil을 출력

var B: String? // 값을 안넣어도 nil을 출력함
// B 는 자동으로 nil 로 설정됨

 

* 옵셔널 바인딩 *

옵셔널 바인딩은 옵셔널 값이 빈값인지 존재하는지 검사한 후,

존재하는 경우 그 값을 다른 변수에 대입시켜 바인딩하는 것을 의미

이렇게 빈 값을 체크하고 옵셔널 값을 언래핑 해주는 것은

강제로 언래핑하는 것보다 훨씬 안전

 

if let / if var, guard let / guard var을 써서

옵셔널 값을 추출해 새로운 변수에 바인딩

 

* if let vs guard let 차이점 *

"if let"은 if문의 코드 구현부 내에서만 상수 사용이 가능 (지역 변수)

"guard let"은 "guard"문을 통과한 상수를 "guard"문 밖에서 사용이 가능 (전역변수)

// 옵셔널 예시
if let <#상수 이름#> = <#옵셔널 값#> {
   // 구현 코드
}

// 옵셔널 예시 1
let A: Int? = nil // A가 있을수도 있고 없을 수 도 있다는 의미, 가장왼쪽이 nil 이기에 A가 존재하지 않는다는것
if let B = A { // 이 표현은 A는 옵셔널 타입 값이며, B에 담아줍니다.(언래핑) 
// if let는 래핑한 박스를 까서 그 값만 담으려는 행위를 말합니다.
    print (B) // 이 부문은 실행이 안되는 것.
}
// 출력값 = 5

// 옵셔널 예시 2
let A1: Int? = 3
if let B1 = A1 {
    print (B1)
}
// 출력값: 3

// 옵셔널 예시 3
// 옵셔널 바인딩 할 변수가 여러 개인 경우
let aName : String?
let bName : String?

aName = "하늘"
bName = "나연"

// , 콤마로 나열합니다
if let a = aName,
   let b = bName {
    print(a, b)
}
// 출력값: 하늘 나연

// 옵셔널 예시 4
let x : Int? = 10
let y : Int? = nil

func opbinding() {
    guard let x = x else { return } 
    // 우변 x는 위에서 선언된 x 좌변 x는 새로만든 변수 혹은 상수 x를 의미
    // else 라는 것은 let x = x 이 부분이 성공을 한다면 else가 실행이 안되며, 실패를 한다면
    // 즉, x에 nil이 들어 있다면 else문이 실행 되어 하위 코드가 실행됩니다.
    print(x)

    guard let y = y else { return } // y는 nil 이므로 여기서 return 
    print(y) // 위에서 return 하였기 때문에 이 코드 라인은 실행되지 않음
}

opbinding()
// 출력값: 10

 

* 옵셔널 강제 언래핑 *

강제 언래핑(Force unwrapping)은 !를 써서 강제로 옵셔널 추출

(변수 앞에 '!'를 붙이는 것은 not의 의미 - 헷갈리지 말것!)

 

강제 언래핑을 잘못 사용할 경우 프로그램이 비정상적으로 종료될 수도 있으므로

반드시 nil이 아닌 것이 확실한 상황에서 사용 (최대한 안쓰것을 선호)

let number = Int("42")! 
/* 
	'Int'안에는 문자열을 넣어 초기화 하는경우 'Int' 값으로 바뀔 수 있는데, 
    이부분은 그냥 '42'가 아니고 옵셔널 '42'의 값입니다. 왜냐하면 변환이 안될 수 있기 때문입니다. 
    변환이 안되면 'nil'을 뱉어주어야 하기 때문이에요.
*/
// String값을 Int로 변환하는 함수는 return값으로 옵셔널 값을 반환합니다.
print(number)
// 출력값: 42

// 강제 언래핑이 실패한 경우
let A: String? = nil
print(A!)
// 에러🚨 메시지: Unexpectedly found nil while unwrapping an Optional value

 

* 옵셔널 변수의 값이 "nil"일 때를 위해 기본값 설정하기 *

값이 'nil'일 경우를 위해 기본값을 설정 가능(nil-coalescing)

'??' 을 사용하여 기본값을 사용할 수 있는데, '??' 이 사용하여

기본값을 부여한 변수는 옵셔널 타입이 아님

 

let(var) a = b ?? c 형태로 이루어지는데 b는 옵셔널 타입이고,

b가 nil일 경우는 a에 c가 대입되고, 'nil'이 아닐 경우에는

옵셔널을 제거한 값이 a에 대입

var optNumber: Int? = 3
let number = optNumber ?? 5 
// optNumber이 옵셔널을 ??로 제거를 했을 때 옵셔널값에 3이 들어가 있는데 3자체가 number로 넘어감
/* 
 오해 금지 -> '??'는 옵셔널을 제거하는게 아닌 기본값을 제공해주는 것이고 
 이미 3이라는 값을 지니고 있는 옵셔널 값이기 때문에 만약 값을 지니고 있지 않다면
 기본값으로 5를 사용한다는 의미가 되는 것 입니다.
*/
print(number) // 출력값 : 3
//number는 'Int?' 타입이 아니라 'Int' 타입

optNumber = nil
let number2 = optNumber ?? 5
print(number) // 출력값 : 5
//number는 Int? 타입이 아니라 Int 타입

print(heartPath)
// imagePaths["heart"]가 nil일 때 
// 출력값: "/images/default.png"

 

* 옵셔널 체이닝 *

옵셔널 체이닝은 옵셔널을 연쇄적으로 사용함

' . ' 을 통해 내부 프로퍼티나 메서드에 연속적으로 접근할 때

옵셔널 값이 있으면 옵셔널 체이닝으로 접근 가능

struct A {
	var name: String
	var address: Address
}

struct B {
	var city: String
	var street: String
	var detail: String
}

let Jun: A? = A(name: "Jun", address: B(city: "서울", street: "신논현로", detail: "100"))
print(Jun.address.city) // 에러. 에러 메시지: Chain the optional using '?' to access member 'address' only for non-'nil' base values
/*
에러가 나는 이유는 '?'를 사용해서 address에 접근해라 라는 의미인데,
쉽게 이야기해서 옵셔널 타입은 내부 속성에 접근할 때 무조건 '?'가 들어가야 하는 것 입니다.
여기서의 '?'란 main과 address는 둘다 옵셔널 타입은 아니잖아요? 하지만
Jun이 A? 옵셔널 타입이기 때문이에요
*/
Jun?.address.city  // ✅ Jun 뒤에 접근할 때는 Jun앞에 '?'를 붙여 주어야 합니다. 
// 즉, Jun이 있을 수도 있고 없을 수도 있어요 옵셔널 A니까요 없다면 뒤에 코드가 실행이 안되고 있다면 한 단계씩 타고 들어갑니다.
// 출력값: 서울

 

만약 "Jun"이 옵셔널이 아닌 "A" 타입이었다면 "Jun.address.city" 가 가능

하지만

"Jun"이 옵셔널 타입이기 때문에 "address"에 접근하려면 "Jun?.address.city" 형태로 되어야 해요.

위 코드 중 “Jun?.address.city” 을 다시 살펴보면,

"Jun == nil"(Jun이 nil) 일 경우 에러가 나지 않고 뒤 코드가 실행안됩니다.

다른 언어의 "null"은 ' . '으로 참조를 하면 바로 "NullPointerException" 에러가 발생

따라서

Swift에서의 "nil"은 메모리의 주소값이 없음을 나타내는 "null"이 아니라 값이 없음을 나타내는 값 을 이야기함

728x90