[Swift๐Ÿฆฉ] #16 Optional Chaining โญ๏ธ

๋˜์ƒยท2022๋…„ 4์›” 1์ผ
0

iOS

๋ชฉ๋ก ๋ณด๊ธฐ
33/47
post-thumbnail
  • Optional Chaining : nil ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” ํ”„๋กœํผํ‹ฐ, ๋ฉ”์„œ๋“œ, ์„œ๋ธŒ์Šคํฌ๋ฆฝํŠธ๋ฅผ ์กฐํšŒํ•˜๊ณ  ํ˜ธ์ถœํ•˜๊ธฐ ์œ„ํ•œ ํ”„๋กœ์„ธ์Šค.
  • ์˜ต์…”๋„์— ๊ฐ’์ด ํฌํ•จ๋˜๋ฉด ์„ฑ๊ณต, nil์ด๋ฉด nil ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค.
  • nil ์ด ๋ฐ˜ํ™˜๋˜๋ฉด ์ฆ‰์‹œ ์ฒด์ธ์ด ์‹คํŒจ๋œ๋‹ค.

1. ๊ฐ•์ œ ์–ธ๋ž˜ํ•‘์˜ ๋Œ€์•ˆ์œผ๋กœ ์‚ฌ์šฉ (?)

  • nil์ด ์•„๋‹Œ ๊ฒฝ์šฐ opt? ์ฒ˜๋Ÿผ ? ๋ฅผ ์‚ฌ์šฉํ•ด์„œ ์˜ต์…”๋„ ์ฒด์ด๋‹์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Œ.
  • opt! ์™€ ๋‹ค๋ฅธ ์ ์€ opt ๊ฐ’์ด nil ์ด์–ด๋„ ๋Ÿฐํƒ€์ž„์—๋Ÿฌ๊ฐ€ ๋‚˜์ง€ ์•Š๋Š”๋‹ค๋Š” ์ ์ด๋‹ค.
  • nil ์ด๋ฉด ์—๋Ÿฌ ๋Œ€์‹  nil ์„ ๋ฐ˜ํ™˜ํ•ด์ค€๋‹ค. -> Optinal ์œผ๋กœ ๋ฐ˜ํ™˜ํ•ด์ฃผ๋Š” ๊ฒƒ.
class Person {
    var residence: Residence?
}

class Residence {
    var numberOfRooms = 1
}

let john = Person()
let roomCount = john.residence!.numberOfRooms // error

// ์•„๋ž˜์˜ roomCount ๋Š” Int? ํƒ€์ž…์ด ๋จ.
// john.residence?.numberOfRooms ๊ฐ€ Optional Chaining
// ๐Ÿ“• if let ๊ตฌ๋ฌธ(Optional Binding) ๊ณผ ๊ฒฐํ•ฉ!
if let roomCount = john.residence?.numberOfRooms {
    // roomCount ๊ฐ€ nil์ด ์•„๋‹ ๋•Œ ์‹คํ–‰.
} else {
    // roomCount ๊ฐ€ nil์ผ ๋•Œ ์‹คํ–‰.
}

john.residence = Residence() // residence != nil



2. ํ”„๋กœํผํ‹ฐ ์ ‘๊ทผ

  • ๋ณต์žกํ•œ ๋ชจ๋ธ ๋‚ด์—์„œ ํ•˜์œ„ ํ”„๋กœํผํ‹ฐ๋กœ ๊ณ„์† ๋‚ด๋ ค๊ฐˆ ์ˆ˜ ์žˆ๊ณ , ํ•˜์œ„ ํ”„๋กœํผํ‹ฐ์˜ ํ”„๋กœํผํ‹ฐ, ๋ฉ”์„œ๋“œ, ์„œ๋ธŒ์Šคํฌ๋ฆฝํŠธ์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.
class Person {
    var residence: Residence?
}

class Residence {
    // ํ”„๋กœํผํ‹ฐ์™€ ์„œ๋ธŒ์Šคํฌ๋ฆฝํŠธ, ๋ฉ”์„œ๋“œ ์ •์˜.
    // rooms ๋ฐฐ์—ด.
    var rooms: [Room] = []
    var numberOfRooms: Int {
        return rooms.count
    }
    subscript(i: Int) -> Room {
        get {
            return rooms[i]
        }
        set {
            rooms[i] = newValue
        }
    }
    func printNumberOfRooms() {
        print("The number of rooms is \(numberOfRooms)")
    }
    var address: Address?

}

class Room {
    let name: String
    init(name: String) { self.name = name }
}

class Address {
    var buildingName: String?
    var buildingNumber: String?
    var street: String?
    func buildingIdentifier() -> String? {
        if let buildingNumber = buildingNumber, let street = street {
            return "\(buildingNumber) \(street)"
        } else if buildingName != nil {
            return buildingName
        } else {
            return nil
        }
    }
}

let john = Person()

// ์•„๋ž˜์ฒ˜๋Ÿผ nil์ผ ์ˆ˜ ์žˆ๋Š” class ์•ˆ์˜ ์š”์†Œ์— chaining ์œผ๋กœ ์ ‘๊ทผ ๊ฐ€๋Šฅ.
if let roomCount = john.residence?.numberOfRooms {
    print("John's residence has \(roomCount) room(s).")
} else {
    print("Unable to retrieve the number of rooms.")
}


// optional chaining ์œผ๋กœ ์–ป์–ด์˜จ ๊ฒƒ์— ๊ฐ’์„ ํ• ๋‹นํ•  ์ˆ˜ ์žˆ๋‹ค.
let someAddress = Address()
someAddress.buildingNumber = "29"
someAddress.street = "Acacia Road"
john.residence?.address = someAddress

// ํ•˜์ง€๋งŒ chaining ์—์„œ nil ์ด ๋‚˜์˜ค๋ฉด ํ• ๋‹น์ด ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์—,
// ํ• ๋‹น์ด ๋œ๊ฑด์ง€ ์•Œ ์ˆ˜ ์žˆ๊ฒŒ ํ•จ์ˆ˜๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
func createAddress() -> Address {
    print("Function was called.")

    let someAddress = Address()
    someAddress.buildingNumber = "29"
    someAddress.street = "Acacia Road"

    return someAddress
}
john.residence?.address = createAddress()



3. ๋ฉ”์„œ๋“œ ํ˜ธ์ถœ

  • ์˜ต์…”๋„ ์ธ์Šคํ„ด์Šค ์•ˆ์˜ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๊ณ , ํ˜ธ์ถœ์ด ๋๋Š”์ง€ ํ™•์ธํ•˜๊ธฐ ์œ„ํ•ด Optional Chaining ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๋ฉ”์„œ๋“œ์— ๋ฐ˜ํ™˜๊ฐ’์ด ์—†์–ด๋„ ์‚ฌ์šฉ ๊ฐ€๋Šฅ. -> ์˜ต์…”๋„ ์ฒด์ด๋‹์ด Void? ์„ ๋ฐ˜ํ™˜ํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ.
func printNumberOfRooms() {
    print("The number of rooms is \(numberOfRooms)")
}


// residence == nil
if john.residence?.printNumberOfRooms() != nil { // nil
    print("It was possible to print the number of rooms.")
} else {
    print("It was not possible to print the number of rooms.")
}

// ํ”„๋กœํผํ‹ฐ ๊ฐ’์„ ์„ค์ •ํ•˜๋Š” ํ‘œํ˜„์‹๋„ ํ‰๊ฐ€ํ•  ์ˆ˜ ์žˆ๋‹ค.
// ํ”„๋กœํผํ‹ฐ ๊ฐ’์ด ์„ค์ •์ด ๋จ -> Void
// ์•ˆ๋จ -> nil ์ด๋ผ ํ™•์ธ ๊ฐ€๋Šฅ.
if (john.residence?.address = someAddress) != nil { // nil
    print("It was possible to set the address.")
} else {
    print("It was not possible to set the address.")
}



4. ์„œ๋ธŒ์Šคํฌ๋ฆฝํŠธ ์ ‘๊ทผ

  • arr?[0] ์ฒ˜๋Ÿผ ๋Œ€๊ด„ํ˜ธ ์ „์— Optional Chaining ์„ ํ•˜๋ฉด ๋œ๋‹ค.
// ์•„๋ž˜ ๋ฌธ์žฅ๊ณผ ๊ฐ™์€ ๋œป. subscript ๋ฅผ ์ •์˜ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— ์•„๋ž˜์ฒ˜๋Ÿผ ์“ธ ์ˆ˜ ์žˆ๋Š” ๊ฒƒ!!
// if let firstRoomName = john.residence?.rooms[0].name {
if let firstRoomName = john.residence?[0].name {
    print("The first room name is \(firstRoomName).")
} else {
    print("Unable to retrieve the first room name.")
}

john.residence?[0] = Room(name: "Bathroom")

์˜ต์…”๋„ ํƒ€์ž…์˜ ์„œ๋ธŒ์Šคํฌ๋ฆฝํŠธ์— ์ ‘๊ทผ

  • dictionary ์˜ ๊ฒฝ์šฐ, key ์•ˆ์— ๋ฐฐ์—ด์ด ๋“ค์–ด์žˆ๋‹ค๋ฉด dict[key][0] ์ด๋Ÿฐ์‹์œผ๋กœ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ๊ทธ๋Ÿฐ๋ฐ, dict[key] ๋Š” Optional ์ด๋ฏ€๋กœ ์—ฌ๊ธฐ์—๋„ Chaining ์ ์šฉ ๊ฐ€๋Šฅ.
var testScores = ["Dave": [86, 82, 84], "Bev": [79, 94, 81]]
testScores["Dave"]?[0] = 91
testScores["Bev"]?[0] += 1
testScores["Brian"]?[0] = 72
// the "Dave" array is now [91, 82, 84] and the "Bev" array is now [80, 94, 81]



5. ์ฒด์ธ ์—ฐ๊ฒฐ

  • ์กฐํšŒํ•˜๋ ค๋Š” ํƒ€์ž…์ด Optional ์ด๋“  ์•„๋‹ˆ๋“  Optional Chain ์˜ ๋ฐ˜ํ™˜๊ฐ’์€ Optional์ด๋‹ค.



6. ์˜ต์…”๋„ ๊ฐ’์„ ๋ฆฌํ„ดํ•˜๋Š” ๋ฉ”์„œ๋“œ ์ฒด์ด๋‹

  • ์˜ต์…”๋„ ์ฒด์ด๋‹์„ ํ†ตํ•ด ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๊ณ ,
  • ๊ทธ ๋ฉ”์„œ๋“œ๊ฐ€ Optional ์„ ๋ฐ˜ํ™˜ํ•œ๋‹ค๋ฉด, ๊ฑฐ๊ธฐ์„œ ์ฒด์ด๋‹์„ ๋˜ ํ•  ์ˆ˜ ์žˆ๋‹ค.
john.residence?.address?.buildingIdentifier()?.hasPrefix("The")



์งˆ๋ฌธ

Optional ์ด๋ž€ ๋ฌด์—‡์ธ์ง€ ์„ค๋ช…ํ•˜์‹œ์˜ค.

  • Swift ์˜ ํƒ€์ž… ์ค‘ ํ•˜๋‚˜๋กœ ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ๊ฑฐ๋‚˜, ๊ฐ’์„ ๊ฐ€์ง€๊ณ  ์žˆ์ง€ ์•Š์€ ๊ฒƒ (nil)
  • ๊ฐ’์„ ์‚ฌ์šฉ ์‹œ, ! ์œผ๋กœ ๋ฒ—๊ฒจ์„œ ์‚ฌ์šฉํ•˜๋ฉฐ
  • ? ์œผ๋กœ ๋ฒ—๊ฒจ์„œ๋„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค. ์ด ๊ฒฝ์šฐ์—” if let ๊ตฌ๋ฌธ์„ ์ด์šฉํ•ด์„œ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌํ•œ๋‹ค.

+) 0401 ์ถ”๊ฐ€ ์ •๋ฆฌ

  • ์•ˆ์ •์„ฑ์„ ๋ณด์žฅํ•˜๋Š” Swift ์–ธ์–ด์˜ ๋Œ€ํ‘œ์ ์ธ ๋ฌธ๋ฒ•์ด๋‹ค.
    • ์˜ˆ๋ฅผ ๋“ค์–ด, C ๊ฐ™์€ ๊ฒฝ์šฐ๋Š” ๋ณ€์ˆ˜๋ฅผ ํ• ๋‹นํ•  ๋•Œ ์ดˆ๊ธฐ๊ฐ’์œผ๋กœ null ์„ ํ• ๋‹นํ•ด์ฃผ์ง€ ์•Š์œผ๋ฉด ์˜ค๋ฅ˜๊ฐ€ ๋‚˜์ง€๋งŒ,
    • Swift๋Š” Optional ํƒ€์ž…์œผ๋กœ ์„ ์–ธํ•ด์ฃผ๋ฉด ์ดˆ๊ธฐํ™”๋ฅผ ํ•˜์ง€ ์•Š์•„๋„ ์ž๋™์œผ๋กœ nil ๊ฐ’์ด ๋“ค์–ด๊ฐ.
  • nil ๋ณ‘ํ•ฉ ์—ฐ์‚ฐ์ž ?? ์„ ์ด์šฉํ•ด์„œ nil ๊ฐ’์ด๋ฉด ๋‹ค๋ฅธ ๊ฐ’์„ ์‚ฌ์šฉํ•˜๊ฒŒ ํ•  ์ˆ˜ ์žˆ๋‹ค.
  • ํด๋ž˜์Šค๋‚˜ ๊ตฌ์กฐ์ฒด ์—ด๊ฑฐํ˜• ์•ˆ์˜ nil ์ผ ๊ฐ€๋Šฅ์„ฑ์ด ์žˆ๋Š” property, method, subscript ... ์— ์ ‘๊ทผํ•  ๋•Œ๋Š”, Optional Chaining ์„ ์‚ฌ์šฉํ•ด์„œ ํƒ€๊ณ  ๋“ค์–ด๊ฐˆ ์ˆ˜ ์žˆ๋‹ค.

<๋ฒ—๋“ค์˜ ์ด์•ผ๊ธฐ>

  • switch ๋กœ๋„ ๋ฒ—๊ธธ ์ˆ˜ ์žˆ๋‹ค.
  • if let vs guard let
    • if let ์€ ๋‹จ์ˆœ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ๋กœ, let ์ดํ•˜๊ฐ€ nil ์ด๋ผ๋„ if let ๊ตฌ๋ฌธ ๋ฐ‘์˜ ๋ฌธ์žฅ๋“ค์€ ์ง€์žฅ ์—†์ด ์‹คํ–‰์ด ๋œ๋‹ค.
    • guard let ์€ ์ด๋ฅธ ์ข…๋ฃŒ ๊ตฌ๋ฌธ์œผ๋กœ, ํ•จ์ˆ˜ ์•ˆ์—์„œ ์‚ฌ์šฉ๋˜๋ฉฐ, guard let ๊ตฌ๋ฌธ์ด nil ์ผ ๊ฒฝ์šฐ else ์ดํ•˜์˜ ์ค‘๊ด„ํ˜ธ ๋ฌธ์žฅ์„ ์‹คํ–‰ํ•˜๊ณ  ๊ทธ ๋ถ€๋ถ„์—์„œ return ํ•ด์„œ ๋๋‚˜๊ฒŒ ๋œ๋‹ค.
  • optional -> generic / enum ์œผ๋กœ ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ.
    • generic ๋‚˜๊ฐ€๊ณ  ๋‚˜์„œ ์ง์ ‘ ๊ตฌํ˜„ํ•ด๋ณด๊ธฐ.



์ถœ์ฒ˜

https://bbiguduk.gitbook.io/swift/language-guide-1/optional-chaining

profile
0๋…„์ฐจ iOS ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค.

0๊ฐœ์˜ ๋Œ“๊ธ€