swift의 키 경로

39816 단어 iosswift
  • 1 - What are Keypaths in swift

  • 2 - Types of KeyPaths
  • 2.1 ReferenceWritableKeyPath
  • 2.2 WritableKeyPath
  • 2.3 KeyPath
  • 2.4 PartialKeyPath
  • 2.5 AnyKeyPath
  • 3 - Swift 5.2: Key path as functions
  • 4 - Using keyPaths
  • See Also
  • Conclusion
  • 1-swift의 관건적인 경로는 무엇입니까


    KeyPath는 특정한 유형의 속성에 대한 인용을 저장하는 대상입니다. 이 KeyPath 대상은 다음에 커서 연산자를 사용하여 기본값을 가져오거나 설정할 수 있습니다. (필요할 때 Getter와setter 값을 지연하거나 숨깁니다.)그리고 키 path 대상을 전달해서 문법이 더 짧고 뚜렷한 API를 만들 수 있습니다.
    예를 들어 한 학급 구성원이 있다고 가정해 봅시다.
    class User {
        var fullName: String
        var email: String?
        var age: Int
    
        init(fullName: String, email: String? = nil, age: Int) {
            self.fullName = fullName
            self.email = email
            self.age = age
        }
    }
    
    지정된 사용자에게는 해당 사용자 유형의 인스턴스 속성(전명, 이메일, 나이)에 대한 키Path 객체를 만든 다음 키Path를 사용하여 특정 사용자 인스턴스의 속성 값을 가져오거나 설정할 수 있습니다.
    let user = User(fullName: "Mike", age: 16)
    let fullNameKeyPath = \User.fullName
    // get fullName value using fullNameKeyPath
    print( user[keyPath: fullNameKeyPath] ) // Mike
    // set fullName value using fullNameKeyPath
    user[keyPath: fullNameKeyPath] = "John"
    print( user[keyPath: fullNameKeyPath] ) // John
    
    보시다시피 키Path 표현식의 형식은 \Root.path입니다. 키Path 성명은
    class KeyPath<Root, Value> : PartialKeyPath<Root>
    
    키Path는 두 가지 일반적인 유형이 있는데 그것이 바로 Root Type(사용자를 예로 들면)과 Value Type이다. 이것은 키Path가 인용한 속성 유형(전체 이름은 String이고 나이는 Int이며 이메일은 String)이다.

    Note: at compile time, a key-path expression ( \Root.path ) is replaced by an instance of the keyPath class of the appropriate keyPath type ( we have 5 types of keypaths )


    또한 keyPath 표현식을 중첩하거나 복합할 수도 있습니다.
    class User {
       // ...
    }
    let user = User(fullName: "Mike", age: 16)
    let fullNameEmptyKeyPath = \User.fullName.isEmpty
    print(user[keyPath: fullNameEmptyKeyPath]) // false
    
    또는
    let user = User(fullName: "Mike", age: 16)
    let fullNamePath = \User.fullName
    let isEmptyPath = \String.isEmpty
    
    let fullNameIsEmptyKeyPath = fullNamePath.appending(path: isEmptyPath) // merge the two paths
    print( user[keyPath: fullNameIsEmptyKeyPath] ) // read keyPath value -> false
    
    여기 키 경로 Root 형식은 User 이고 Value 형식은 Bool (is Empty는 Bool) 이기 때문에 전체 이름은 Is Empty Key Path 형식은 KeyPath<User, Bool> 이다.

    2. 주요 경로의 유형


    우리는 5가지 유형의 키 경로가 있는데 5개의 범형류로 구성된 차원 구조는 ReferenceWritableKeyPath 계승자WritableKeyPathWritableKeyPath 계승자KeyPath 등이다.

    Note: that hierarchy means ( for example ) if you have a function that takes KeyPath<Root,Type> as a parameter then you can pass also a keyPath of type WritableKeyPath and ReferenceWritableKeyPath as a parameter to hat function because both of theme inherits from KeyPath<Root,Type> but you cannot pass in a parameter of type PartialKeyPath or AnyKeyPath



    서로 다른 관건적인 경로 유형을 하나하나 탐색해서 언제 사용할 수 있는지 봅시다.

    2.1 ReferenceWritableKeyPath

    ReferenceWritableKeyPath는 참조 유형(클래스) 및 소프트 속성(var)의 루트와 함께 작동하며 속성 값을 읽고 쓰기(변경)할 수 있습니다.
    이것은 사용자가 class/reference 형식이고fullName 속성은 var이기 때문에 앞에서 보았습니다.
    class User {
       var fullName: String
        var email: String?
        var age: Int
      // ...
    }
    let fullNameKeyPath = \User.fullName
    print(type(of: fullNameKeyPath)) // ReferenceWritableKeyPath<User, String>
    

    Note: it doesn't matter wether the instance itself is var or let, what matters is the keyPath property should be mutable ( var ) to be able to change/write its value.


    2.2 쓰기 가능한 키 경로

    WritableKeyPathworks는 값 형식(struct와enum)의 루트로 사용되며, 실례의 키Path 속성 값을 다시 읽거나 쓸 수 있도록 합니다. 또한 속성은 var이고 실례도 var이어야 합니다.
    struct User {
        var fullName: String
        var email: String?
        var age: Int
    }
    var user = User(fullName: "Mike", age: 16) // instance should be var
    let ageKeyPath = \User.age
    // get fullName
    print(user[keyPath: ageKeyPath]) // 16 
    // set fullName
    user[keyPath: ageKeyPath] = 20
    print(user[keyPath: ageKeyPath]) // 20
    
    print(type(of: ageKeyPath)) // WritableKeyPath<User, Int>
    

    2.3 주요 경로


    KeyPath는 인용 형식 (클래스) 과 값 형식 (struct와 enum) 과 함께 사용할 수 있는 가장 간단한 형식입니다.이것은 속성 값을 읽는 데만 사용되기 때문에 변경되지 않는 속성에 접근하는 데만 사용됩니다.
    struct User {  // or it can be -> class User 
        var fullName: String
        var email: String?
        let age: Int // Note: age is immutable
    }
    var user = User(fullName: "Mike", age: 16)
    let ageKeyPath = \User.age
    // get age
    print(user[keyPath: ageKeyPath]) // 16
    print(type(of: ageKeyPath)) // KeyPath<User, Int>
    
    만약 우리가 Root type 를 사용하여 속성 값을 변경하려고 시도한다면.
    user[keyPath: ageKeyPath] = 20  // error: cannot assign through subscript: 'ageKeyPath' is a read-only key path
    

    2.4 PartialKeyPath


    앞의 세 가지 키 경로 유형 중에서 우리는 두 가지 필수 유형을 알고 있다. 즉 ageKeyPath (우리의 예시에서 사용자) 와 Root type (문자열은 전체 이름을 표시하거나 인트는 나이를 표시한다).그러나 만약에 우리가 키 경로의 Value Type를 모르거나 값 유형을 더욱 유연하게 처리해야 한다면 우리는 Value type를 사용할 수 있다. 이것은 특정PartialKeyPath<Root>에서 어떤 ValueType속성도 인용할 수 있다.서로 다른 값 형식이지만 같은 루트 형식의 키 경로를 받아들이기 위해 함수를 작성해야 하거나, 하나의 그룹에 서로 다른 값 형식의 여러 키 경로를 저장해야 할 때, 이것은 매우 유용하다.
    struct User {
        var fullName: String
        var email: String?
        let age: Int
    }
    
    var user = User(fullName: "Mike", email: "[email protected]", age: 16)
    
    let agePartialKeyPath: PartialKeyPath<User> = \User.age
    let emailPartialKeyPath: PartialKeyPath<User> = \User.email
    let fullNamePartialKeyPath: PartialKeyPath<User> = \User.fullName
    
    let keyPathsList: [PartialKeyPath<User>] = [agePartialKeyPath,emailPartialKeyPath,fullNamePartialKeyPath]
    keyPathsList.forEach { keypath in
        print(user[keyPath: keypath]) // 16, Optional("[email protected]"), Mike
    }
    

    2.5 AnyKeyPath


    AnyKeyPath는 제한이 가장 적습니다.Root type 알 수 없고 Root Type (경로) 알 수 없을 때 전체 형식의 지우는 키 경로를 사용할 수 있습니다.다른 루트 형식의 키 경로를 사용하는 API를 만들거나, 하나의 그룹에 다른 루트 형식의 여러 키 경로를 저장하려면 매우 유익합니다.
    struct Cat {
        var name: String
    }
    
    class User {
       // ...
    }
    
    var user = User(fullName: "Mike", email: "[email protected]", age: 16)
    
    let userAgeKeyPath = \User.age // ReferenceWritableKeyPath<User, Int>
    let userEmailKeyPath = \User.email // ReferenceWritableKeyPath<User, Optional<String>>
    let userFullNameKeyPath = \User.fullName // ReferenceWritableKeyPath<User, String>
    
    var cat = Cat(name: "Nala")
    let catNameKeyPath = \Cat.name // WritableKeyPath<Cat, String>
    
    let keyPathsList: [AnyKeyPath] = [userAgeKeyPath,userEmailKeyPath,userFullNameKeyPath,catNameKeyPath]
    
    keyPathsList.forEach { keypath in
        if let keypath = keypath as? PartialKeyPath<User> {
            print(user[keyPath: keypath]) // // 16, Optional("[email protected]"), Mike
        }else if let keypath = keypath as? PartialKeyPath<Cat> {
            print(cat[keyPath: keypath]) // Nala
        }
    
    }
    
    여기서 서로 다른 값 유형과 서로 다른 루트 유형의 키 경로를 만들고 AnyKeyPath 유형의 배열에 저장합니다.
    AnyKeyPath를 사용하기 때문에 키 path 루트 형식이나 값 형식에 대한 정보를 알 수 없습니다. 이것이 바로 우리가 Value Typekeypath (Getter가any로 되돌아오는 이유) 입니다.우리는 당연히 그것을 더욱 구체적인 키 경로로 전환할 수 있다. 예를 들어 PartialKeyPath<User> 또는 KeyPath<Cat, String>. 만약 전환에 성공한다면, 우리는 KeyPath<User, String>Root Type 를 정확하게 알 것이다.
    다시 한 번 살펴보자.
    키 슬롯 유형
    읽다
    쓰다
    메모
    임의의 키 경로
    예, 그렇습니다.
    아니오.
    알 수 없는 루트 유형/알 수 없는 값 유형
    PartialKeyPath
    예, 그렇습니다.
    아니오.
    알 수 없는 루트 유형/알 수 없는 값 유형
    키홈
    예, 그렇습니다.
    아니오.
    알려진 루트 유형/알려진 값 유형
    쓰기 가능 키 경로
    예, 그렇습니다.
    예, 그렇습니다.
    값 형식 (struct와 enum) 의 알 수 있는 루트 형식/알 수 있는 값 형식 (var 실례)
    ReferenceWritableKeyPath
    예, 그렇습니다.
    예, 그렇습니다.
    알 수 있는 루트 형식/알 수 있는 값 형식, 클래스 형식 (let 또는 var 실례) 에 사용

    3-Swift 5.2: 주요 경로 기능


    형식: Value Type의 클립은 Swift에서 흔히 볼 수 있기 때문에 키 경로는 함수로서 Swift 5.2에 추가된 기능 중의 하나이다.키 path를 자동으로 업그레이드할 수 있으며, 폼 (Root Type ) -> Value 기능이 필요한 곳에서도 사용할 수 있습니다.
    만약 우리가 사용자 목록을 가지고 있다면, 우리는 그들의 전체 이름을 추출해야 한다.
    struct User {
        var fullName: String
        var email: String?
        var age: Int
    }
    
    var firstUser = User(fullName: "Mike", email: "[email protected]", age: 20)
    var secondUser = User(fullName: "Adam", email: "[email protected]", age: 25)
    var ThirdUser = User(fullName: "John", email: "[email protected]", age: 30)
    var users = [firstUser,secondUser,ThirdUser]
    
    let usersNames = users.map { $0.fullName } // original
    print(usersNames) // ["Mike", "Adam", "John"]
    
    여기서 우리는 지도에 함수를 전달한다. 그 형식은 (Root ) -> Value이고 사용자의 전체 이름을 되돌려준다. swift 5.2에서 우리는 사용자의 전체 이름 속성의 키 경로를 전달하고 이를 지도에 전달할 수 있다.
    let usersNames = users.map (\.fullName) // using keypath
    print(usersNames)  // ["Mike", "Adam", "John"]
    
    또는
    let fullNameKeyPath: (User) -> String = \User.fullName
    let usersNames = users.map ( fullNameKeyPath )
    print(usersNames)
    
    그러나 이렇게 하면 우리는 키 경로 유형( User ) -> String을 명확하게 설명해야 한다. 왜냐하면 let fullNameKeyPath: (User) -> String은 함수 (기본 행위) 가 아니라 키 경로의 대상으로 추정되기 때문이다.형식을 현식화함으로써 키 path 대상이 아닌 함수로 사용할 것입니다.
    let fullNameKeyPath = \User.fullName // inferred as WritableKeyPath<User, String>
    let usersNames = users.map ( fullNameKeyPath ) // Error
    print(usersNames)
    

    4. 주요 경로 사용


    컬렉션 유형에서 키 경로를 사용하여 문법이 더 짧고 명확한 일반 알고리즘과 API를 만들 수 있습니다.예를 들어, 우리는 어떤 유형의 요소를 포함하는 그룹을 정렬하고, 특정한 속성에 따라 정렬하려고 한다.열쇠 경로가 있어,
    extension Sequence {
        func sorted<T: Comparable>(by keyPath: KeyPath<Element, T>) -> [Element] {
            return sorted { a, b in
                return a[keyPath: keyPath] < b[keyPath: keyPath]  // read
            }
        }
    }
    
    var firstUser = User(fullName: "Mike", email: "[email protected]", age: 20)
    var secondUser = User(fullName: "Adam", email: "[email protected]", age: 25)
    var ThirdUser = User(fullName: "John", email: "[email protected]", age: 30)
    var users = [firstUser,secondUser,ThirdUser]
    
    print( users.sorted(by: \.fullName))
    // [User(fullName: "Adam", email: Optional("[email protected]"), age: 25),
    // User(fullName: "John", email: Optional("[email protected]"), age: 30),
    // User(fullName: "Mike", email: Optional("[email protected]"), age: 20)]
    print( ["C","D","AB","A"].sorted(by: \.self) ) // ["A", "AB", "C", "D"]
    print( ["C","D","AB","A"].sorted(by: \.count) ) // ["C", "D", "A", "AB"]
    
    여기에 우리는 let fullNameKeyPath = \User.fullName 유형을 토대로 fullNameKeyPath를 만들었고 새로운 extension를 추가했다. 키 경로를 사용하여 키 경로 값에 따라 그룹을 정렬할 수 있기 때문에 우리는 전체 이름, 이메일, 나이에 따라 사용자 그룹을 정렬하거나 유사한 속성에 따라 모든 그룹을 정렬할 수 있다.
    또 다른 예는 짧은 구문으로 UIlabel 속성을 업데이트하려는 경우 다음과 같은 작업을 수행할 수 있습니다.
    protocol Builder {}
    
    extension Builder {
         func set<T>(_ keyPath:ReferenceWritableKeyPath<Self, T>,to newValue: T) -> Self {
            self[keyPath: keyPath] = newValue  // change property value
            return self
        }
    }
    
    extension UILabel: Builder { }
    
    let label = UILabel()
        .set(\.text, to: "Hello world")
        .set(\.textColor, to: .red)
        .set(\.font, to: .systemFont(ofSize: 24, weight: .medium))
    

    Note: if you want to apply this to any UIKit component, just make it conform to the Builder protocol just like we did with the UILabel like extension extension UIButton: Builder { }. but if we want to use this on any NSObject or any UIKit element then we can instead make the NSObject conforms to the Builder protocol extension NSObject: Builder { }.


    보시다시피, 관건적인 경로는 레이아웃 제약, 집합 형식 알고리즘, 그리고 많은 다른 용례를 처리하는 데 사용되는 DSL을 만드는 데 비교적 짧은 문법을 가진 API를 많이 만들 수 있습니다.

    따로 만나다


  • KeyPathKit는 키 경로에 의존하는 문법으로 데이터를 조작하여 호출을 더욱 짧고 뚜렷하게 할 수 있는 많은 함수를 제공했다.
  • KeyPaths in Swift

  • Smart KeyPaths: Better Key-Value Coding for Swift proposal
  • Key Path Expressions as Functions Proposal
  • 결론


    Swift 4에 도입된 주요 경로는 Swift 3의 구형Sequence 문법 제한을 극복했다.#keyPath()는 여러 제한이 있는 문자열로 표시되는 문자열인 것을 알 수 있습니다.
  • sorted(by keyPath:) NSObjects
  • 에만 해당
  • 유형 정보가 분실되었습니다. 키 경로가 어떤 유형(문자열, Int 등)을 가리키는지 모릅니다
  • 이전 #keyPath ()에 비해 키Path는 매우 훌륭하고 유형이 안전하기 때문에 우리는 더욱 간단하고 간결한 통용 알고리즘과 API를 만들 수 있다.SwiftUI 및 Combine 프레임워크에서도 키 경로를 사용하여 API를 단순하게 유지합니다.
    만약 내가 무엇을 놓쳤다고 생각한다면, 나에게 알려주고, 언제든지 당신의 생각이나 피드백을 나에게 공유해 주십시오.
    읽어주셔서 감사합니다!🚀

    좋은 웹페이지 즐겨찾기