* 값이 없음을 포함하는 옵셔널의 개념 *
[ 옵셔널과 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"이 아니라 값이 없음을 나타내는 값 을 이야기함
'IOS > Swift-Study' 카테고리의 다른 글
[Swift-Study] 기초 문법종합반 1주차 2일차 정리 - 배열, 세트, 딕셔너리 (0) | 2024.03.12 |
---|---|
[Swift-Study] 기초 문법종합반 1주차 2일차 정리 - 스택, 큐 (0) | 2024.03.12 |
[Swift-Study] 기초 문법종합반 1주차 1일차 정리 - 연산자, 조건문과 반복문 (2) | 2024.03.11 |
[Swift-Study] 기초 문법종합반 1주차 1일차 정리 - 데이터 타입 (0) | 2024.03.11 |
[Swift-Study] 기초 문법종합반 1주차 1일차 정리 - 프로퍼티와 출력, 함수 (0) | 2024.03.11 |