상세 컨텐츠

본문 제목

[Day11] Optional(2)

Swift&SwiftUI

by (방울)도마토 2024. 4. 22. 14:41

본문

https://bangul-domato.tistory.com/35

 

[Day10] Optional (1)

Optional - 언어 차원에서 프로그램의 안전성을 높이기 위해 사용하는 개념 - 'nil'을 사용할 수 있는 타입과 사용할 수 없는 타입을 구분하고, 사용할 수 있는 타입을 가리켜 Optional Type으로 부름 nil

bangul-domato.tistory.com

 

옵셔널 강제 해제

-  옵셔널 타입 값 뒤에 '!' 기호만 붙여주면 됨

    - 옵셔널 객체 해제, 내부에서 저장된 값을 꺼내 사용할 수 있게 됨 

- '!' : Forced-Unwrapping Operator

 

var optInt: Int? = 3

print("옵셔널 자체의 값 : \(optInt)") // Optional(3)
print("! 강제 해제 값 : \(optInt!)") // 3

 

- 옵셔널 타입끼리 연산은 불가능하지만, 강제 해제 연산자를 사용하면 일반 타입으로 해제되므로 연산 가능 

Int("123")! + Int("123")! // 246
Int("123")! + 30 // 153

 

- 강제 해제 연산자를 사용할 때는 옵셔널 값이 nil인지 점검

    - 옵셔널 값이 nil이 아닐 때만 강제 해제 연산자를 붙여서 값을 추출해야 함 

var str = "123"
var intFromStr = Int(str) // Optional(123)

if intFromStr != nil {
    print("변환된 값: \(intFromStr!)") // 123
} else {
    print("값 변환 실패")
}


var str = "Swift"
var intFromStr = Int(str) // nil

if intFromStr != nil {
    print("변환된 값: \(intFromStr!)") 
} else {
    print("값 변환 실패") // 값 변환 실패
}

 

- else 블록에서 ! 연산자를 사용하면 nil에 대한 옵셔널 강제 해제가 실행되어 오류가 발생 

    - ! 연산자는 확실히 옵셔널의 값이 nil이 아닌 조건에서만 사용 

 

- intFromStr 와 비교 연산자 != 사이에 의도적인 공백

intFromStr!=nil 
// 1. (intFromStr)!=nil -> intFromStr 변수와 nil 사이의 비교
// 2. (intFromStr!)=nil -> intFromStr 변수의 옵셔널 강제 해제 + nil 값의 할당

→ 옵셔널 타입이 비교 대상이라면 부등 비교 연산자를 사용할 때 공백을 두어야 함 

 

 

Optional Binding

- 조건문 내에서 일반 상수에 옵셔널 값을 대입하는 방식

- 반드시 조건문에서 사용, 상수에 옵셔널 값을 대입한 결과는 true/false로 리턴

var str = "Swift"
if let intFromStr = Int(str) {
    print("반환된 값 : \(intFromStr)")
} else {
    print("값 변환 실패") // 값 변환 실패
}

- intFromStr 이 상수로 선언, 상수가 옵셔널이 아닌 일반 타입이 앞의 구문과 다른 점

    - 강제 해제 연산자를 사용하지 않아도 옵셔널 값이 일반 변수나 상수에 할당되면서 자연스럽게 옵셔널 타입 해제

    - 값이 nil이더라도 값의 할당이 실패하여 결과값이 false로 반환될 뿐이므로 오류는 발생하지 않음, else 블록이 실행 

func intStr(str: String) {
    guard let intFromStr = Int(str) else {
        print("값 변환 실패")
        return
    }
    
    print("변환된 값: \(intFromStr)")
}

- guard 구문은 조건에 맞지 않으면 무조건 함수의 실행을 종료 

- 실행 흐름상 옵셔널 값이 해제되지 않으면 더이상 진행이 불가능할 정도로 큰일이 생길 때에만 사용하는 것이 좋음 

(참고로 옵셔널 타입이긴 하지만, 절대 nil 값이 들어가지 않을 것이란 보장이 있을 때에만 강제 해제 연산자를 사용하여 옵셔널 타입 처리)

 

var capital = ["KR" : "Seoul", "EN" : "London", "FR" : "Paris"]

print(capital["KR"])  // Optional("Seoul")
print(capital["KR"]!) // Seoul


// 보완된 코드 
if (capital["KR"] != nil) {
    print(capital["KR"]!)
} 
if let val = capital["KR"] {
    print(val)
}

- 딕셔너리가 옵셔널 타입의 값을 반환하는 이유 

    - 딕셔너리의 키 → Hashable 프로토콜이 구현된 모든 자료형 

    - 무작위로 키가 사용될 수도 있고, 키가 있는지 점검하기도 어려움 

        - 존재하지 않는 키를 사용할 가능성 존재 → 기본적으로 옵셔널 타입으로 정의된 값을 반환하게 됨 

  

* 옵셔널 타입에서 ! 연산자 사용시 반드시 nil 점검을 해주어야 오류를 미연에 방지할 수 있음 

 

 

 

컴파일러에 의한 옵셔널 자동해제

- 일반적으로 옵셔널 타입의 값을 사용하려면 항상 ! 연산자를 사용해 옵셔널을 강제 해제(Forced-unwrapping) or 옵셔널 바인딩을 통해 일반 자료형으로 바꾸어 주어야 함 

- 명시적으로 강제 해제하지 않아도 컴파일러에서 자동으로 옵셔널을 해제해 주는 경우 존재 

let optInt = Int("123")

// 강제해제 하지 않은 옵셔널의 값 -> Optional(123) != int 123
if ((optInt) == 123) {
    print("optInt == 123")  // optInt == 123
} else {
    print("optInt != 123")
}

- 옵셔널 객체의 값을 비교 연산자를 사용하여 비교하는 경우 

- 명시적으로 옵셔널 객체를 강제 해제하지 않아도 한쪽이 옵셔널, 다른 한쪽이 일반 타입이라면 자동으로 옵셔널 타입을 해제하여 비교 연산을 수행 

let tempInt = Int("123")

tempInt == 123 // true
tempInt == Option(123) // true
tempInt! == 123 // true
tempInt! == Option(123) // true
var optValue01 = Optional(123) // 1. 원칙
var optValue02 = Int? = 123 // 2. 순수 리터럴을 직접 대입

- 타입 어노테이션 없이 직접 대입하면 단순한 일반 Int 타입의 변수로 선언 

    → 타입 어노테이션을 추가하여 Int 타입이면서 동시에 옵셔널 타입이라는 것을 명시 

 

 

옵셔널의 묵시적 해제(Implicitly Unwrapped Optional)

// 명시적 옵셔널 선언 
var str: String? = "Swift Optional"
print(str)
    // Optional("Swift Optional")


// 묵시적 옵셔널 선언 
var str: String! = "Swift Optional"
print(str)
    // Swift Optional

 

var value01: Int? = 10 
value01 + 5 // 오류 

// 묵시적 해제 선언 
var value02: Int! = 10 
value02 + 5 // 15

- 묵시적 해제를 선언한 옵셔널 타입은 일반 옵셔널 타입과 달리 직접 일반 타입과 연산해도 문제가 되지 않음 

    - 묵시적 해제를 선언한 옵셔널은 일반 타입처럼 사용할 수 있기 때문에 굉장히 편리하게 사용할 수 있음 

 

* 변수 값이 nil이 될 가능성이 있다면 묵시적 옵셔널 해제를 사용하지 않아야 함 

* 묵시적 옵셔널 해제를 사용하는 경우 

- 형식상 옵셔널로 정의해야 하지만, 실제로 사용할 때에는 절대 nil 값이 대입될 가능성이 없는 변수일 때 

 

- 실제로 묵시적 옵셔널이 정말 유용하게 사용되는 경우 

    - 클래스 또는 구조체 내에서 멤버 변수를 정의할 때 선언과 초기화를 분리시켜야 하는 경우 

 

- 옵셔널의 강점은 안정성뿐만 아니라 안정성을 담보하는 과정에서 표현되는 코드의 간결성에 있음 

ex. Optional Chain

// Objective-C 
if (myDelegate != nil) {
    if ([myDelegate respondsToSelector:@selector(scrollViewDidScroll:)]) {
        [myDelegate scrollViewDidScroll:myScrollView];
    }
}

// swift 
myDelegate?.scrollViewDidScroll?(myScrollView)

관련글 더보기

댓글 영역