본문 바로가기

iOS-Study

iOS Study : 8주차 : 문법 세부 정리 - 옵셔널 체이닝(Optional Chaining)

옵셔널 체이닝(Optional Chaining)

옵셔널 체이닝이란 옵셔널일 수 있는 인스턴스 내부의 프로퍼티, 메서드, 서브 스크립트를 매번 nil인지 아닌지
체크를 하지 않고 최종적으로 원하는 값, 혹은 nil인지 판단하는 방법이다.

 

간단한 예시

옵셔널 체이닝을 사용하지 않았을 때

class Family {
    var child: Child?
}

class Child {
    var name: String?
}

var family: Family? = Family()
var child: Child? = Child()
child?.name = "아들"

func noOptionalChaining(child: Child?) {
  if let child = child {
    if let name = child.name {
      print(name)
    }
    else {
      print("이름이 존재하지 않는다.")
    }
  }
}

noOptionalChaining(child: child)
//아들 출력

 

옵셔널 체이닝을 사용하는 과정

class Family {
    var child: Child?
}

class Child {
    var name: String?
}

var family: Family? = Family()
family?.child?.name = "아들"

let name = family?.child?.name
print(name)	//nil
  1. Family 클래스 안에는 Child 옵셔널 타입 변수, Child 클래스 안에는 String 옵셔널 타입 변수가 하나씩 있다.
  2. Family 인스턴스를 생성하고 name에 값을 할당해 준다. 이 때 family 인스턴의 child 프로퍼티는 옵셔널 타입이므로
    ? 를 붙여준다.
  3. 값을 할당한 인스턴스를 name 변수에 넣어서 출력을 하면 할당한 값이 아닌 nil 값이 반환이 된다. 왜 그럴까?

nil 값이 나온 이유

옵셔널 체이닝에서 체인이라는 단어와 연관이 있는데 child 프로퍼티들은 서로 연결이 되어 있다.
그래서 Child 클래스의 name이 옵셔널이든 값이 할당이 되어있든지 상관 없이, 값에 접근하기 위해 사용했던 
family 인스턴스의 프로퍼티인 child가 옵셔널이기 때문에 최종 값은 옵셔널이 되게 되고, 그 값은 기본적으로 nil이 되는 것이다.

name 값 출력하기

name의 값을 출력하려면 Child 인스턴스를 생성하여 값을 할당해 연결해 주면 된다.

class Family {
    var child: Child?
}

class Child {
    var name: String?
}

var family: Family? = Family()
var child: Child? = Child()
child?.name = "아들"

family?.child = child

let name = family?.child?.name
print(name)	//Optional("아들")

Child 인스턴스를 생성하여 값을 할당해 준 후 연결하여 출력하면 Optional("아들")이 출력된다. 이는 Family 클래스의 child 프로퍼티가 옵셔널 타입이기 때문인데 옵셔널을 해제해 주면 된다. 여기에서 옵셔널 체이닝을 사용할 수 있다.

 

옵셔널 체이닝 사용후 출력까지

class Family {
    var child: Child?
}

class Child {
    var name: String?
}

var family: Family? = Family()
var child: Child? = Child()
child?.name = "아들"

family?.child = child

if let name = family?.child?.name {
    print(name)
} else {
    print("이름이 존재하지 않는다.")
}
//아들 출력

 

다른 방식

nil 병한 연산자로 중위 연산자 ?? 를 사용하여 출력할 수도 있다.

옵셔널의 값이 nil일 경우 ?? 뒤의 값을 반환한다.

class Family {
    var child: Child?
}

class Child {
    var name: String?
}

var family: Family? = Family()
var child: Child? = Child()
child?.name = nil

family?.child = child

var name = family?.child?.name ?? "이름이 존재하지 않는다."
print(name) //값이 nill 이므로 이름이 존재하지 않는다 출력

 

심화 예시

위에서 본 간단한 예시는 클래스 안에 프로퍼티가 적었지만 만약 프로퍼티가 많아지게 되면 옵셔널 체이닝이 필수적이게 된다.

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
    }
}

Person 클래스와 Aprtment 클래스를 만든 상태에서 각 클래스 안의 프로퍼티중 몇개는 연결이 되어있다. 이를 가지고
아파트의 경비원의 직업이 무엇인지 확인을 해 볼 것이다.

 

옵셔널 체이닝을 사용하지 않은 경우

let lee: Person? = Person(name: "lee")
let apart: Apartment? = Apartment(dong: "308", ho: "203")
let superman: Person? = Person(name: "superman")

func guardJob(owner: Person?) {
    if let owner = owner {	//사람이 존재하는지 확인 
        if let home = owner.home {	//그 사람이 집이 있는지 확인
            if let `guard` = home.guard {	//그 집에 경비원이 있는지 확인
                if let guardJob = `guard`.job {	//그 경비원이 직업이 있는지 확인
                    print("우리집 경비원의 직업은 \(guardJob)입니다")
                } else {
                    print("우리집 경비원은 직업이 없어요")
                }
            }
        }
    }
}
guardJob(owner: yagom)

옵셔널 체이닝을 사용하면 위와 같이 매우 복잡해진다. 하지만 옵셔널 체이닝을 사용하면 아래처럼 간단하게 표현 가능하다.

 

옵셔널 체이닝을 사용한 경우

let lee: Person? = Person(name: "lee")
let apart: Apartment? = Apartment(dong: "308", ho: "203")
let superman: Person? = Person(name: "superman")

func guardJobWithOptionalChaining(owner: Person?) {
    if let guardJob = owner?.home?.guard?.job {
        print("우리집 경비원의 직업은 \(guardJob)입니다")
    } else {
        print("우리집 경비원은 직업이 없어요")
    }
}
guardJobWithOptionalChaining(owner: lee)
//우리집 경비원은 직업이 없어요

 

 


참고 블로그

공식문서

야곰닷넷

블로그