Programming/Swift

[Swift] 문법 다시보기 - 프로퍼티

devssun 2021. 10. 12. 16:48
728x90
반응형

야곰 스위프트 프로그래밍 책을 읽으며 기억해둘 문법을 정리합니다.


프로퍼티

타입 프로퍼티

  • 각각의 인스턴스가 아닌 타입 자체에 속하는 프로퍼티, 타입 자체에 영향을 미친다

  • 저장 타입 프로퍼티 → 변수 또는 상수 선언

  • 연산 타입 프로퍼티 → 변수로만 선언

  • 저장 타입 프로퍼티 → 반드시 초깃값 설정, 지연 연산, 지연 저장 프로퍼티와는 조금 다르게 다중 스레드 환경이라고 하더라도 단 한 번만 초기화된다는 보장을 받는다.

  • 코드

      class AClass {
    
              // 저장 타입 프로퍼티
          static var typeProperty: Int = 0
    
              // 저장 인스턴스 프로퍼티
          var instanceProperty: Int = 0 {
              didSet {
                              // Self.typeProperty는
                              // AClass.typeProperty와 같은 표현이다
                  Self.typeProperty = instanceProperty + 100
              }
          }
    
              // 연산 타입 프로퍼티
          static var typeComputedProperty: Int {
              get {
                  return typeProperty
              }
              set {
                  typeProperty = newValue
              }
          }
      }
    
      AClass.typeProperty = 123
    
      let classInstance = AClass()
      classInstance.instanceProperty = 100
    
      print(AClass.typeProperty) // 200
      print(AClass.typeComputedProperty) // 200

키 경로

  • 프로퍼티의 위치만 참조하도록 할 수 있는 방법, 키 경로(Key Path) 활용

  • 키 경로 타입은 AnyKeyPath라는 클래스로부터 파생

  • 제일 많이 확장된 키 경로 타입은 WritableKeyPath<Root, Value>ReferenceWritableKeyPath<Root, Value> 타입

  • WritableKeyPath<Root, Value> 타입은 값 타입에 키 경로 타입으로 읽고 쓸 수 있는 경우에 적용

  • ReferenceWritableKeyPath<Root, Value> 타입은 참조 타입, 즉 클래스 타입에 키 경로 타입으로 읽고 쓸 수 있는 경우에 적용

  • 키 경로는 \, 타입, 마침표 경로로 구성 (ex. \타입이름.경로.경로)

  • 키 경로를 잘 활용하면 타입 간의 의존성을 낮추는 데 많은 도움을 준다

  • 코드

      // 키 경로 타입의 타입 확인
      class Person {
          var name: String
    
          init(name: String) {
              self.name = name
          }
      }
    
      struct Stuff {
          var name: String
          var owner: String
      }
    
      print(type(of: \Person.name)) // ReferenceWritableKeyPath<Person, String>
      print(type(of: \Stuff.name))  // WritableKeyPath<Stuff, String>
    
      // 키 경로 타입의 경로 연결
      let keyPath = \Stuff.owner
      let nameKeyPath = keyPath.appending(path: \.name)
      // keyPath 서브스크립트와 키 경로 활용
    
      class Person {
          let name: String
    
          init(name: String) {
              self.name = name
          }
      }
    
      struct Stuff {
          var name: String
          var owner: Person
      }
    
      print(type(of: \Person.name))
      print(type(of: \Stuff.name))
    
      let yagom = Person(name: "yagom")
      let hana = Person(name: "hana")
      let macbook = Stuff(name: "MacBook Pro", owner: yagom)
      var iMac = Stuff(name: "iMac", owner: yagom)
      let iPhone = Stuff(name: "iPhone", owner: hana)
    
      let stuffNameKeyPath = \Stuff.name
      let ownerkeyPath = \Stuff.owner
    
      // \Stuff.ower.name과 같은 표현이 된다
      let ownerNameKeyPath = ownerkeyPath.appending(path: \.name)
    
      // 키 경로와 서브스크립트를 이용해 프로퍼티에 접근하여 값을 가져온다
      print(macbook[keyPath: stuffNameKeyPath])   // MacBook Pro
      print(iMac[keyPath: stuffNameKeyPath])      // iMac
      print(iPhone[keyPath: stuffNameKeyPath])    // iPhone
    
      print(macbook[keyPath: ownerNameKeyPath])   // yagom
      print(iMac[keyPath: ownerNameKeyPath])      // yagom
      print(iPhone[keyPath: ownerNameKeyPath])    // hana
    
      // 키 경로와 서브스크립트를 이용해 프로퍼티에 접근하여 값을 변경한다
      iMac[keyPath: stuffNameKeyPath] = "iMac Pro"
      iMac[keyPath: ownerkeyPath] = hana
    
      print(iMac[keyPath: stuffNameKeyPath])      // iMac Pro
      print(iMac[keyPath: ownerNameKeyPath])      // hana
    
      // 상수로 지정한 값 타입과 읽기 전용 프로퍼티는 키 경로 서브스크립트로도 값을 바꿀 수 없다
      // 클로저를 대체할 수 있는 키 경로 표현
      struct Person {
          let name: String
          let nickname: String?
          let age: Int
    
          var isAdult: Bool {
              return age > 18
          }
      }
    
      let yagom: Person = Person(name: "yagom", nickname: "bear", age: 100)
      let hana = Person(name: "hana", nickname: "na", age: 100)
      let happy = Person(name: "happy", nickname: nil, age: 3)
    
      let family = [yagom, hana, happy]
      // {$0.name}의 표현과 같은 역할을 수행한다
      let names: [String] = family.map(\.name)  // ["yagom", "hana", "happy"]
      let nicknames: [String] = family.compactMap(\.nickname)  // ["bear", "na"]
      let adults: [String] = family.filter(\.isAdult).map(\.name) // ["yagom", "hana"]
반응형