swift의 키 경로
2 - Types of KeyPaths
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
계승자WritableKeyPath
와 WritableKeyPath
계승자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 typeWritableKeyPath
andReferenceWritableKeyPath
as a parameter to hat function because both of theme inherits fromKeyPath<Root,Type>
but you cannot pass in a parameter of typePartialKeyPath
orAnyKeyPath
서로 다른 관건적인 경로 유형을 하나하나 탐색해서 언제 사용할 수 있는지 봅시다.
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 쓰기 가능한 키 경로
WritableKeyPath
works는 값 형식(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 Type
을 keypath
(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 theUILabel
like extensionextension 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 protocolextension NSObject: Builder { }
.
보시다시피, 관건적인 경로는 레이아웃 제약, 집합 형식 알고리즘, 그리고 많은 다른 용례를 처리하는 데 사용되는 DSL을 만드는 데 비교적 짧은 문법을 가진 API를 많이 만들 수 있습니다.
따로 만나다
KeyPathKit는 키 경로에 의존하는 문법으로 데이터를 조작하여 호출을 더욱 짧고 뚜렷하게 할 수 있는 많은 함수를 제공했다.
결론
Swift 4에 도입된 주요 경로는 Swift 3의 구형
Sequence
문법 제한을 극복했다.#keyPath()는 여러 제한이 있는 문자열로 표시되는 문자열인 것을 알 수 있습니다.sorted(by keyPath:)
NSObjects만약 내가 무엇을 놓쳤다고 생각한다면, 나에게 알려주고, 언제든지 당신의 생각이나 피드백을 나에게 공유해 주십시오.
읽어주셔서 감사합니다!🚀
Reference
이 문제에 관하여(swift의 키 경로), 우리는 이곳에서 더 많은 자료를 발견하고 링크를 클릭하여 보았다 https://dev.to/ahmed_komsan12/keypaths-in-swift-18o6텍스트를 자유롭게 공유하거나 복사할 수 있습니다.하지만 이 문서의 URL은 참조 URL로 남겨 두십시오.
우수한 개발자 콘텐츠 발견에 전념 (Collection and Share based on the CC Protocol.)