아삭아삭 iOS 개발

[Swift 독학] 패스트캠퍼스 챌린지 33일차_옵셔널 체이닝(1) 본문

Swift

[Swift 독학] 패스트캠퍼스 챌린지 33일차_옵셔널 체이닝(1)

바닐라머스크 2022. 2. 25. 22:52

 

오늘은 swift에서 여러 옵셔널 내부의 값들 nil일지 아닐지 모르는 요소를 유용하게 활용할 있는 방법인 옵셔널 체이닝에 대해 알아보겠습니다.

옵셔널 체이닝의 정의와 다양한 활용방안 약 8가지에 대해 2편의 포스팅으로 나누어서 살펴보겠습니다.

 

반응형

 

■ 옵셔널 체이닝

  • (옵셔널에 속해 있는) nil일지도 모르는 프로퍼티, 메서드, 서브스크립션 등을 호출하거나 조회하기 위한 일련의 과정
  • 옵셔널에 값이 있다면 프로퍼티, 메서드 등을 호출 가능
  • 옵셔널이 nil이라면 프로퍼티, 메서드 등은 nil을 반환
    ※ 여러 조회는 함께 연결될 수도 있으며, 체인에 어느 부분이라도 nil이면 전체 체인은 실패함
  • 옵셔널 체이닝의 ?는 항상 옵셔널 표현구 다음에 위치함

 

■ 옵셔널 체이닝의 다양한 활용방법 8가지 (1) ~ (4)

 

1) 옵셔널 체이닝을 통해 모델 클래스 정의하기

 

  • 이상의 레벨 depth 갖는 프로퍼티, 메서드, 서브 스크립트를 호출하기 위해 옵셔널 체이닝 사용 가능
  • 타입이 호환되는 복잡한 모델 내에서 하위단의 프로퍼티로 내려가기 가능
    → 내려간 하위 프로프티에서 프로퍼티, 메서드, 서브 스크립트에 접근 가능여부 확인 가능

 

 

 

 

 

class Nomad {
    var location: Location?
}

// Location 클래스는 [Country] 타입의 빈 배열을 갖고 초기화 되는 countries 라는 변수 프로퍼티를 정의
class Location {
    var countries: [Country] = []
    var numberOfCountries: Int {
        return countries.count
    }
    subscript(i: Int) -> Country {
        get {
            return countries[i]
        }
        set {
            countries[i] = newValue
        }
    }
    func printNumberOfCountries() {
        print("The number of countries where I've lived so far is \(numberOfCountries).")
    }
    var continent: Continent?
}
// Country 인스턴스의 배열을 저장하기 때문에, numberOfCountries 프로퍼티는 연산 프로퍼티로 구현
// 연산 numberOfCountries 프로퍼티는 countries배열에서 count프로퍼티의 값을 반환
// countries배열에 접근하는 짧은 구문을 위해 countries 배열에 요청된 인덱스로 나라들에 접근을 제공하는 읽기-쓰기 서브 스크립트를 제공함
// 거주하는 나라의 이름을 출력하는 printNumberOfCountries 메서드도 제공
// Continent? 타입을 갖는 continent라는 옵셔널 프로퍼티를 정의

class Country {
    let name: String
    init(name: String) { self.name = name }
}
// countries 배열에 사용된 Country 클래스는 name 프로퍼티와 적절한 국가명을 프로퍼티에 설정하는 초기화 구문으로 구성

class Continent {
    var continentName: String?
    var wellknownRiverName: String?
    var stateReligionName: String?
    func countryIdentifier() -> String? {
        if let wellknownRiverName = wellknownRiverName, let stateReligionName = stateReligionName {
            return "name of representative river is \(wellknownRiverName) and state of religion is \(stateReligionName)."
        } else if continentName != nil {
            return continentName
        } else {
            return nil
        }
    }
}
// String? 타입의 3개의 옵셔널 프로퍼티를 가짐
// Continent 클래스는 String? 반환타입을 갖는 countryIdentifier() 라는 메서드를 제공
// countryIdentifier 메서드는 대륙의 프로퍼티를 확인하고 값이 있으면 continentName을 반환하거나 둘 다 값이 있으면 stateReligionName과 연결된 wellknownRiverName 을 반환하고, 값이 없으면 nil 을 반환함

 

 

2) 옵셔널 체이닝을 통해 옵셔널 프로퍼티에 접근하기

  • 내부 프로퍼티에 접근하기 전에 ? 나 ! 를 사용하여 접근
    ① ? 로 옵셔널 체이닝을 한 경우
        - 접근한 프로퍼티의 값은 항상 옵셔널로 감싸져 있는 값이 출력됨
           ∵ 출력값이 nil일 수도 있으므로
    ② ! 로 옵셔널 체이닝을 한 경우
        - 접근한 프로퍼티의 값은 옵셔널로 감싸져 있지 않은 값이 출력됨
          ∵ 옵셔널 프로퍼티를 강제로 언래핑하여 접근했으므로
let vanillaMusk = Nomad()

if let countryCount = vanillaMusk.location?.numberOfCountries {
    print("The number of countries where I've lived so far is \(countryCount).")
} else {
    print("Unable to know where she/he is living in now.")
}
// Unable to know where she/he is living in now.
// vanillaMusk.location는 nil 이므로 옵셔널 체이닝은 이전처럼 실패 호출

// 옵셔널 체이닝을 통해 프로퍼티 값 설정 가능
let someContinent = Continent()
someContinent.wellknownRiverName = "Han river"
someContinent.stateReligionName = "free"
vanillaMusk.location?.continent = someContinent
// vanillaMusk.location가 nil 이므로 vanillaMusk.location에 continent프로퍼티를 설정하기 위한 시도는 실패
// = 연산자의 우항코드는 평가되지 않으므로 옵셔널 체이닝의 일부

func createContinent() -> Continent {
    print("Function was called.")

    let someContinent = Continent()
    someContinent.wellknownRiverName = "Han river"
    someContinent.stateReligionName = "free"

    return someContinent
}

vanillaMusk.location?.continent = createContinent()
// 위와 동일한 할당 작업을 수행하지만 Continent를 생성하기 위해 함수를 사용
// 반환하기 전에 "Function was called."를 출력하여 = 연산자의 우항이 평가되었음을 확인 가능
// 실행결과, 아무것도 출력되지 않았기 때문에 createContinent() 함수 미호출로 확인

 

 

3)옵셔널 체이닝을 통해 함수를 호출하기

  • 옵셔널 값의 메서드를 호출하고 메서드 호출이 성공적인지 확인하기 위해 옵셔널 체이닝 사용 가능
    ※ 메서드가 반환값을 정의하지 않아도 사용 가능
  • 옵셔널 체이닝을 사용하여 옵셔널 값에 메서드를 호출하면, 메서드의 반환 타입은 Void 아닌 Void?
    ※ 
    반환값은 옵셔널 체이닝을 통해 호출하면 옵셔널 타입이므로
    → 반환 타입이 없는 함수와 메서드는 Void 암시적 반환 타입을 갖음
    → () 또는 튜플을 반환한다는 의미
  • 옵셔널 체이닝을 사용하여 (메서드가 반환값을 정의하지 않아도) if구문으로 메서드 호출 가능여부 확인 가능
     
    메서드 호출의 반환값을 nil 비교함으로써
  • 옵셔널 체이닝을 통해 프로퍼티 설정시, nil 비교하여 프로퍼티에 값이 성공적으로 설정되었는지 확인가능한 Void? 타입의 값을 반환함
func printNumberOfCountries() {
    print("The number of countries where I've lived so far is \(numberOfCountries).")
}
// 위에 언급된 Location 클래스의 printNumberOfCountries() 메서드는 numberOfCountries의 현재값을 출력함

if vanillaMusk.location?.printNumberOfCountries() != nil {
    print("It was possible to print the number of countries.")
} else {
    print("It was not possible to print the number of countries.")
}
// It was not possible to print the number of countries.
// if 구문을 통해서 (메서드가 반환값을 정의하지 않아도) printNumberOfCountries() 메서드 호출 가능여부 확인 가능
// printNumberOfCountries 호출 반환값을 nil과 비교하여 확인

if (vanillaMusk.location?.continent = someContinent) != nil {
    print("It was possible to set the continent.")
} else {
    print("It was not possible to set the address.")
}
// It was not possible to set the address.
// 옵셔널 체이닝을 통해 프로퍼티를 설정하려는 모든 시도를 nil 과 비교하여 '프로퍼티에 값 설정 성공여부' 확인 가능

 

 

4)옵셔널 체이닝을 통해 메서드를 호출하기

  • 옵셔널 체이닝을 통해 메서드를 호출 가능
  • 메서드 반환값에서 추가로 옵셔널 체이닝을 수행하고자 경우, 메서드의 소괄호 이후 옵셔널 체이닝 ? 표기하면
let vanillaMuskLocation = Continent()
vanillaMuskLocation.continentName = "Asia"


if let
countryIdentifier = vanillaMusk.location?.continent?.countryIdentifier() {
    print("VanillaMusk's continent Identifier is \(countryIdentifier).")
}
// VanillaMusk's continent Identifier is Asia.

if let startsWithThe =
    vanillaMusk.location?.continent?.countryIdentifier()?.hasPrefix("The") {
    if startsWithThe {
        print("VanillaMusk's country identifier starts with \"The\".")
    } else {
        print("VanillaMusk's country identifier does not start with \"The\".")
    }
}
// VanillaMusk's country identifier does not start with "The".
// 예제에서 연결하려는 옵셔널 값은 countryIdentifier() 메서드의 반환값이기 때문에 소괄호 뒤에 옵셔널 체이닝 ?를 위치시킴

 

 

다음 포스팅에서는 옵셔널 체이닝의 서브 스크립트 접근, 여러 레벨로 체인 연결, nil 병합 연산자 그리고 강제언래핑 대체용으로의 사용방법에 대해 정리해보겠습니다~ :)

 

 

본 게시물은 개인 공부 기록용이므로 내용에 오류가 있을 수 있습니다.

 fast campus강의  참조자료

[1] https://blog.yagom.net/553/

[2] https://bbiguduk.gitbook.io/swift/language-guide-1/enumerations

 

https://bit.ly/37BpXiC

 

패스트캠퍼스 [직장인 실무교육]

프로그래밍, 영상편집, UX/UI, 마케팅, 데이터 분석, 엑셀강의, The RED, 국비지원, 기업교육, 서비스 제공.

fastcampus.co.kr

 

포스팅은 패스트캠퍼스 환급 챌린지 참여를 위해 작성되었습니다.