옵셔널(Optional)2

임혜정·2024년 6월 8일
0

특정 변수나 상수에 '해당 값이 없을 수 있음'을 직관적으로 표현가능하고 값이 없음을 나타내는 nil을 안전하게 처리할 수 있도록 한다. 옵셔널이 없다면 nil을 처리하는 과정에서 런타임 에러(프로그램이 실행되는 동안에 발생하는 에러)를 발생시킬 것이다.

var 이름: String? 

이름이 String으로 된 값을 가질 수도 있고, nil(없음)일 수도 있다는 뜻

var Box: String?

if let name = Box {
    print("\(name)")
} else {
    print("nil") // 결과값: nil
}

위와 같은 경우에는 Box에 String타입의 값이 없기 때문에 else구문이 실행된다.

어떨 때 사용해야하지?

해당 타입의 값이 없을 가능성이 있을 때이다. 예를 들어 네트워크 요청, 데이터베이스 쿼리 등 외부 데이터 소스에서 값을 가져올 때 없을 가능성에 대비하여 안전하게 처리한다.


옵셔널 값의 처리 방법

  1. 강제 언래핑(Forced Unwrapping):
    느낌표(!)를 사용하여 옵셔널 값을 강제로 언래핑할 수 있는데 값이 nil일 경우 런타임 에러가 발생할 수 있다. 따라서 확신할 수 있을 때만 사용한다.
var name: String? = "혜정"
print(name!)
  1. 옵셔널 바인딩(Optional Binding)
    불확실한 값에 대해 안전하게 언래핑할 수 있도록 한다. if let 또는 guard let구문을 사용한다.
var 이름: String? = "혜정"

if let 언래핑 = 이름 {
    print(언래핑)
} else {
    print("이름이 없어요..")
}
  1. nil 병합 연산자(nil-coalescing operator)
    ??를 사용해서 값이 nil일 경우에 뱉어줄 기본 값을 제공
var 이름: String?

let 언래핑이름 = 이름 ?? "몰라요" //nil일 경우 몰라요 출력
print(언래핑이름)
  1. 옵셔널 체이닝(Optional Chaining)
    옵셔널 값에 여러 단계를 거쳐 접근할 때 중간에 하나라도 nil이라면 전체 결과를 nil로 하는 방법이다.
class 사람 {
    var 이름: String
    var 애완동물: 고양이?
    
    init(이름: String, 애완동물: 고양이? = nil) {
        self.이름 = 이름
        self.애완동물 = 애완동물
    }
}

class 고양이 {
    var 이름: String
    var 수염: String?
    
    init(이름: String, 수염: String? = nil) {
        self.이름 = 이름
        self.수염 = 수염
    }
}

let 사람과애완동물 = 사람(이름: "혜정", 애완동물: 고양이(이름: "야옹잉", 수염: "하얀색"))
let 사람만 = 사람(이름: "혜정")

// 옵셔널 체이닝을 사용하여 안전하게 접근
if let 고양이수염 = 사람과애완동물.애완동물?.수염 {
    print("혜정이네 고양이의 수염색깔은 \(고양이수염)")
} else {
    print("혜정이네 고양이는 수염이 없어요")
}

if let 고양이수염 = 사람만.애완동물?.수염 {
    print("혜정이네 고양이의 수염색깔은 \(고양이수염)")
} else {
    print("혜정이네 고양이는 수염이 없어요")
}

'사람과애완동물.애완동물?.수염' 은 애완동물이 있고 그 애완동물의 수염이 무슨 색인지 확인한다. nil일 경우 "수염이 없어요"를 출력한다.

'사람만.애완동물?.수염' 은 애완동물이 없기 때문에 nil에 해당하는 값을 출력한다.


추가

암시적 언래핑 옵셔널(Implicitly Unwrapped Optional)
옵셔널 타입이지만 값을 사용할 때 자동으로 언래핑되는 특성을 가진다. 옵셔널 값이 항상 존재함을 확신할 때 사용해야한다

예를 들어 초기화 후 값이 변경되지 않는 경우, 디자인 상 항상 값이 존재하는 경우 (ex. IBOutlet), 초기 설정이 완료된 후 사용되는 경우

1) 초기화 후 값이 변경되지 않는 경우

class ViewController {
    var data: String!
    
    func viewDidLoad() {
        super.viewDidLoad()
        data = "데이터 초기화" // 초기화, 이 이후에 nil이 아닐 것이 확실해진다. 
    }
    
    func displayData() {
        print(data) 
    }
}

2) 디자인 상 항상 값이 존재 ex. IBOutlet

class ViewController: UIViewController {

    @IBOutlet var label: UILabel!
    // UILabel! 이라고 암시적 옵셔널이 선언되었다. 스토리보드에 연결되므로(일부러 연결을 끊지 않는 이상)뷰가 로드된 후에는 nil이 아닐 것이 보장된다.
    
    override func viewDidLoad() {
        super.viewDidLoad()
        label.text = "Hello, World!"
        // 따라서 이 부분에서 label을 언래핑해주지 않아도 사용할 수 있는 것
    }
}

음..1의 강제 옵셔널의 설명과 헷갈리는데?

둘다 값의 존재를 확신할 수 있을 때에만 사용하라는 느낌은 비슷하다. 그렇지 않으면 런타임에러를 일으키는 것도.
그러나 암시적 옵셔널 언래핑은 정의할 때 사용되어서 호출될 때는 자동으로 언래핑된다는 것, 강제 언래핑은 호출 시점에 사용되어 언래핑 된다는 것.

// 암시적 언래핑 옵셔널
var name: String! = "hj" // 선언할 때 해놓고 나아간다

// 값을 부를 때 그냥 일반 변수처럼 부른다.
print(name) // "hj"

// 값이 nil일 경우에는 런타임 에러
name = nil
// print(name) // 런타임 에러 발생: Unexpectedly found nil while implicitly unwrapping an Optional value
var name: String? = "hj" 

// 강제 언래핑
print(name!) // "hj"

// 값이 nil일 경우에는 런타임 에러
name = nil
// print(name!) // 런타임 에러 발생: Unexpectedly found nil while unwrapping an Optional value
profile
오늘 배운걸 까먹었을 미래의 나에게..⭐️

0개의 댓글