λ³Έλ¬Έ λ°”λ‘œκ°€κΈ°

🍎 iOS/Swift

[Swift] Optional

μ•ˆλ…•ν•˜μ„Έμš” μ œμΈμž…λ‹ˆλ‹€ :)

TIL을 μž‘μ„±ν•˜λ‹€κ°€ μ˜΅μ…”λ„μ— λŒ€ν•΄ 정리λ₯Ό ν•˜κ²Œ λ˜μ—ˆλŠ”λ°μš”,

κ·Έλƒ₯ μ˜΅μ…”λ„ λ½€κ°œκΈ°λ‘œ μ œλŒ€λ‘œ 정리λ₯Ό ν•˜μž μ‹Άμ–΄μ„œ Optional에 λŒ€ν•œ κ³΅μ‹λ¬Έμ„œμ™€ κΉƒν—ˆλΈŒμ— μ˜€ν”ˆλœ Optional λ‚΄λΆ€ ꡬ쑰 μ½”λ“œλ₯Ό ν† λŒ€λ‘œ μ˜΅μ…”λ„μ— λŒ€ν•΄ 정리λ₯Ό μ­‰ 해보렀 ν•©λ‹ˆλ‹€!

Optional (μ˜΅μ…”λ„)

μ˜΅μ…”λ„μ€ 값이 'μžˆμ„ μˆ˜λ„, 없을 μˆ˜λ„ 있음' μ„ λ‚˜νƒ€λ‚΄λŠ” ν‘œν˜„μž…λ‹ˆλ‹€.
μ΄λŠ” 'λ³€μˆ˜λ‚˜ μƒμˆ˜ 등에 κΌ­ κ°’이 μžˆλ‹€λŠ” 것을 보μž₯ν•  수 μ—†μŠ΅λ‹ˆλ‹€. μ¦‰, λ³€μˆ˜ λ˜λŠ” μƒμˆ˜μ˜ 값이 nil일 μˆ˜λ„ μžˆλ‹€'λŠ” 것을 의미

 

@frozen
enum Optional<Wrapped>: ExpressibleByNiliteral {
    case none // nil
    case some(Wrapped) // optional value
}

 

μ˜΅μ…”λ„μ€ μ œλ„€λ¦­μ΄ 적용된 μ—΄κ±°ν˜• νƒ€μž…μž…λ‹ˆλ‹€.
μ΄λŠ” μ˜΅μ…”λ„μ΄ κ°’을 κ°–λŠ” μΌ€μ΄μŠ€μ™€ 그렇지 λͺ»ν•œ μΌ€μ΄μŠ€ 두 κ°€μ§€λ‘œ μ •μ˜λ˜μ–΄ μžˆμŒμ„ λœ»ν•©λ‹ˆλ‹€.

 

μ°Έκ³ ) @frozen(Declaration Attributes 쀑 ν•˜λ‚˜)을 ꡬ쑰체(struct) λ˜λŠ” μ—΄κ±°ν˜•(enum) Declaration에 μ μš©ν•˜λ©΄ νƒ€μž… 변경을 μ œν•œν•  수 μžˆλ‹€. enum νƒ€μž… κ³ μ •λ˜λ―€λ‘œ switchλ¬Έ μ“Έ λ•Œ default μΌ€μ΄μŠ€κΉŒμ§€ 써쀄 ν•„μš” μ—†μŒ!

μ˜΅μ…”λ„μ„ μ‚¬μš©ν•˜λŠ” 이유

λͺ…μ‹œμ  ν‘œν˜„

1. nil의 κ°€λŠ₯성을 μ½”λ“œλ§ŒμœΌλ‘œ ν‘œν˜„κ°€λŠ₯(직관적)

2. λ¬Έμ„œ/주석 μž‘μ„± μ‹œκ°„ μ ˆμ•½

 

μ•ˆμ „ν•œ μ‚¬μš©

1. 전달받은 값이 μ˜΅μ…”λ„μ΄ μ•„λ‹ˆλΌλ©΄ nil 체크λ₯Ό ν•˜μ§€ μ•Šκ³  μ‚¬μš©κ°€λŠ₯

2. μ˜ˆμ™Έ 상황을 μ΅œμ†Œν™” ν•˜λŠ” μ•ˆμ „ν•œ μ½”λ”©

3. 효율적 μ½”λ”©

Optional Unwrapping(μ˜΅μ…”λ„ μΆ”μΆœ)

μ˜΅μ…”λ„ νƒ€μž…μ˜ 값을 μΆ”μΆœν•˜λŠ” 방법에 λŒ€ν•΄ μ•Œμ•„λ΄…μ‹œλ‹€!

1. Optional Binding(μ˜΅μ…”λ„ 바인딩)

λ¨Όμ € μ˜΅μ…”λ„ 값을 nil인지 μ•„λ‹Œμ§€ 체크(μ˜΅μ…”λ„ μ•ˆμ— 값이 λ“€μ–΄μžˆλŠ”μ§€ 확인)ν•˜κ³ , 값이 있으면 κ·Έ 값을 κΊΌλ‚΄μ˜€λŠ” μ•ˆμ „ν•œ μΆ”μΆœ λ°©λ²•μž…λ‹ˆλ‹€.

if let κ³Ό guard let ꡬ문을 μ΄μš©ν•΄ μ˜΅μ…”λ„ λ°”μΈλ”©μœΌλ‘œ μ•ˆμ „ν•˜κ²Œ μ˜΅μ…”λ„ 값을 μΆ”μΆœν•  수 μžˆμŠ΅λ‹ˆλ‹€.

 

// if let ꡬ문 μ‚¬μš© μ˜ˆμ‹œ
if let starPath = imagePaths["star"] {
    print("The star image is at '\(starPath)'")
} else {
    print("Couldn't find the star image")
}

// guard let ꡬ문 μ‚¬μš© μ˜ˆμ‹œ
guard let starPath = imagePaths["star"] else {
    print("Couldn't find the star image")
    return
}

print("The star image is at '\(starPath)'")

 

if letκ³Ό guard let에 λŒ€ν•œ 더 μƒμ„Έν•œ μ„€λͺ…κ³Ό 두 ꡬ문의 차이점에 λŒ€ν•΄μ„œλŠ” μ•„λž˜ κ²Œμ‹œκΈ€μ„ μ°Έκ³ ν•΄μ£Όμ‹œλ©΄ 쒋을 것 κ°™μŠ΅λ‹ˆλ‹€ :)

 

[Swift] if let과 guard let의 차이

μ•ˆλ…•ν•˜μ„Έμš” μ œμΈμž…λ‹ˆλ‹€!😊 μ˜€λŠ˜μ€ if letκ³Ό guard let의 차이점에 λŒ€ν•΄ ν•œλ²ˆ 정리해보렀고 ν•©λ‹ˆλ‹€. Optional νƒ€μž…μ˜ λ³€μˆ˜λ₯Ό μ‚¬μš©ν•  λ•ŒλŠ” μ˜΅μ…”λ„ 바인딩을 톡해 μ•ˆμ „ν•˜κ²Œ κΊΌλ‚΄μ„œ μ“°λŠ”κ²Œ 정말 μ€‘μš”ν•œλ°

janechoi.tistory.com

2. Optional Chaning(μ˜΅μ…”λ„ 체이닝)

λž˜ν•‘λœ μΈμŠ€ν„΄μŠ€μ˜ ν”„λ‘œνΌν‹°μ™€ λ©”μ„œλ“œμ— μ•ˆμ „ν•˜κ²Œ μ•‘μ„ΈμŠ€ν•˜λ €λ©΄ ? μ—°μ‚°μžλ₯Ό μ΄μš©ν•œ μ˜΅μ…”λ„ 체이닝λ₯Ό μ‚¬μš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.

μ˜΅μ…”λ„ 체이닝은 μ˜΅μ…”λ„ λ‚΄λΆ€μ˜ λ‚΄λΆ€μ˜ λ‚΄λΆ€λ‘œ μ€‘μ²©λ˜μ–΄ μ˜΅μ…”λ„μ΄ μ—°κ²°λ˜μ–΄ μžˆμ„ λ•Œ μœ μš©ν•˜κ²Œ ν™œμš©ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
맀번 nil 확인을 ν•˜μ§€ μ•Šκ³  μ΅œμ’…μ μœΌλ‘œ μ›ν•˜λŠ” 값이 μžˆλŠ”μ§€ μ—†λŠ”μ§€ 확인할 수 μžˆμŠ΅λ‹ˆλ‹€.
μ€‘μ²©λœ μ˜΅μ…”λ„ 쀑 ν•˜λ‚˜λΌλ„ 값이 μ‘΄μž¬ν•˜μ§€ μ•ŠλŠ”λ‹€λ©΄ 결과적으둜 nil을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

 

// μ‚¬λžŒ 클래슀
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 jane: Person? = Person(name: "jane")
let apart: Apartment? = Apartment(dong: "101", ho: "202")
let superman: Person? = Person(name: "superman")

// μ˜΅μ…”λ„ 체이닝을 μ‚¬μš©ν•΄ ν”„λ‘œνΌν‹°μ— μ ‘κ·Ό
func guardJobWithOptionalChaining(owner: Person?) {
    if let guardJob = owner?.home?.guard?.job {
        print("μš°λ¦¬μ§‘ κ²½λΉ„μ›μ˜ 직업은 \(guardJob)μž…λ‹ˆλ‹€")
    } else {
        print("μš°λ¦¬μ§‘ 경비원은 직업이 μ—†μ–΄μš”")
    }
}

guardJobWithOptionalChaining(owner: jane)
// μš°λ¦¬μ§‘ 경비원은 직업이 μ—†μ–΄μš”

3. Nil-Coalescing Operator(nil 병합 μ—°μ‚°μž)

Optional μΈμŠ€ν„΄μŠ€κ°€ nil일 경우 default값을 μ œκ³΅ν•˜κΈ° μœ„ν•΄ nil 병합 μ—°μ‚°μž(??)λ₯Ό μ‚¬μš©ν•©λ‹ˆλ‹€.

??을 μ‚¬μš©ν•˜μ—¬ ν‘œν˜„ν•˜λŠ” μ€‘μœ„ μ—°μ‚°μžμ΄κΈ° λ•Œλ¬Έμ— (Optional) ?? (Value) μ™€ 같이 optional κ°’κ³Ό value 사이에 μœ„μΉ˜ν•©λ‹ˆλ‹€.
쒌츑의 μ˜΅μ…”λ„ 값이 nil일 경우, 우츑의 값을 λ°˜ν™˜ν•©λ‹ˆλ‹€.

 

titleLabel.text = UserDefaults.standard.value(forKey: "nickname") ?? "μ‚¬μš©μž"

let input = readLine()
print("μž…λ ₯ 값은 \(input ?? "") μž…λ‹ˆλ‹€")

4. Unconditional Unwrapping(μ˜΅μ…”λ„ κ°•μ œ μΆ”μΆœ)

μ˜΅μ…”λ„μ— 값이 λ“€μ–΄μžˆλŠ”μ§€ μ•„λ‹Œμ§€ ν™•μΈν•˜μ§€ μ•Šκ³  κ°•μ œλ‘œ 값을 κΊΌλ‚΄λŠ” λ°©μ‹μž…λ‹ˆλ‹€.

μ˜΅μ…”λ„ μΈμŠ€ν„΄μŠ€μ— 값이 ν¬ν•¨λ˜μ–΄ μžˆλ‹€κ³  ν™•μ‹ ν•˜λŠ” 경우 κ°•μ œ μ–Έλž© μ—°μ‚°μž(!)λ₯Ό μ‚¬μš©ν•˜μ—¬ 무쑰건 값을 μ–Έλž˜ν•‘ν•  수 μžˆμŠ΅λ‹ˆλ‹€.
λ§Œμ•½ 값이 μ—†μ„κ²½μš°(nil) λŸ°νƒ€μž„ 였λ₯˜κ°€ λ°œμƒν•©λ‹ˆλ‹€.

 

var myName: String? = jane
var youName: String! = nil


printName(myName!) // jane 좜λ ₯
myName = nil

print(myName!)
// κ°•μ œμΆ”μΆœμ‹œ 값이 μ—†μœΌλ―€λ‘œ λŸ°νƒ€μž„ 였λ₯˜ λ°œμƒ

yourName = nil

printName(yourName)
// nil 값이 μ „λ‹¬λ˜κΈ° λ•Œλ¬Έμ— λŸ°νƒ€μž„ 였λ₯˜λ°œμƒ

 


[참고 자료]

https://developer.apple.com/documentation/swift/optional

https://github.com/apple/swift/blob/main/stdlib/public/core/Optional.swift