- 애플 문서에서 KVO를 객체의 accessor methods를 통한 접근보다 더 flexiblie하고 general mechanism을 제공한다고 설명한다.
Introduction
Using Key-Value Coding Compliant Objects
1. Access object properties
2. Manipulate collection properties
3. Invoke Collection operators on collection objects
4. Access non-object properties
Other Cocoa Tchnologies Rely on Key-Value Coding
1. Key-value objserving
2. Cocoa bindings
3. Core Data
4. AppleScript
1. Accessing Object Properties
Listing 1-1)
@inetrface BankAccount : NSObject
@property (nonatomic) NSNumber *currentBalance;
@property (nonatomic) Person *owner;
@property (nonatomic) NSArray<Transaction *>* transactions;
@end
KVO를 통해 [myAccount setValue:@(100.0) forKey:@"currentBalance"]; 로 properties에 접근할 수 있다.
또한, keyPath string( ex. @"owner.address" )을 통해 property의 property를 참조할 수 있다. 즉, keyPath string을 통해 객체 맨 하단 hierarchy까지 접근하여 해당 property들을 참조할 수 있는것이다.
Getting Attribute Values Using Keys
- valueForKey:
: 키에 대한 해당 값을 가져옴. 값이 존재하지 않으면 valueForUndefinedKey: 메서드가 불리게 되고 NSObject default로 구현된 동작은 NSUndefinedKeyException을 발생시키는 것이다. 값이 존재하지 않을때의 동작을 Custom하게 구현하려면 해당 메서드를 오버라이드 하면된다.
- valueForKeyPath:
: keyPath에 대한 값을 가져옴. 값이 존재하지 않을경우의 동작은 valueForKey: 같다.
- dictionaryWithValuesForKeys:
: Key들에대한 Value들의 array를 return한다.
* Listing 1-1)에서 property "transactions"이 payee라는 객체들을 포함하고 있다면, keyPath로 "transactions.payee"를 전달하면 transactions에 포함되있는 모든 payee객체들이 array로 return된다. 또한, accounts가 BankAccount의 array property라면 keyPath "accounts.transactions.payee"를 전달하면 accounts안에 모든 transaction의 모든 payee를 array로 return한다.
2. AccessingCollection Properties
Collection properties에 대해서 KVC를 쓸때 해당 데이터를 immutable하게 다루는 것보다 mutable하게 다루는 것이 사용하는데 있어서 편리할때가 많다.
해서, KVC는 NSMutableArray, NSMutableSet, NSMutableOrderedSet을 return하는 메서드를 제공한다. 해당 메서드를 사용해서 adding, removing, replacing동작을 할 경우 해당 데이터를 reference하고 있는 객체에서도 동일하게 작업이 반영된다.
- NSMutableArray: mutableArrayValueForKey:, mutableArrayValueForKeyPath:
- NSMutableSet: mutableSetValueForKey:, mutableSetValueForKeyPath:
- NSMutableOrderedSet: mutableOrderedSetValueForKey, mutableOrderedSetValueForKeyPath:
3. Using Collection Operators
valueForKeyPath: 메서드를 호출할 때, keyPath안에 Collection Operator를 넣을 수 있다.
* Operator key path format
"keypathToCollection.@collectionOperator.keyPathToProperty"
Listing 3-1) Linsting 1-1)의 BankAccount.transactions에 포함되는 Tranaction객체.
@interface Transaction : NSObject
@property (nonatomic) NSString *payee;
@property (nonatomic) NSNumber* amount;
@property (nonatomic) NSDate* date
@end
Aggregation Operators
@avg
: 각 값을 double형으로 변환해서 더한다음 평균값을 구해서 return. 각 값에 대해서 nil인 경우 0으로 변환해서 계산.
NSNumber *transactionAverage = [[self transactions] valueForKeyPath:@"@avg.amount"];
@count
: 객체의 갯수 값 return, right keypath 필요없음. 있으면 무시한다.
NSNumber *numberOfTransactions = [[self transactions] valueForKeyPath:@"@count"];
@max
: 값 중 제일 큰 값을 return. 값 비교는 "compare:" 메서드를 통해 수행함. 고로, right keyPath가 가리키는 property는 "compare:" 메시지에 응답할 수 있는 객체여야 한다.
@min
: 제일 작은 값 return. 동작방식은 @max랑 동일.
@sum
: 값들의 합을 반환. 각 값을 double형으로 반환한 후 계산. 각 값에 대해서 nil인 경우 0으로 변환해서 계산.
NSNumber *amountSum = [[self transactions] valueForKeyPath:@"@sum.amount"];
Array Operators
: valueForKeyPath: 메서드에서 Array Operators를 사용할때, leaf Object들 중 하나라도 nil인 경우 exception을 발생시킴.
@distinctUnionOfObjects
: 합집합, 중복제거
NSArray *distinctPayees = [[self transactions] valueForKeyPath:@"@distinctUnionOfObjects.payee"];
@unionOfObjects
: 합집합
NSArray *payees = [[self transactions] valueForKeyPath:@"@unionOfObjects.payee"];
Nesting Operators
: nesting collecction들에 대해서 동작하는 operators이다.
: valueForKeyPath: 메서드에서 Nesting Operators를 사용할 때, leaf Object들 중 하나라도 nil인 경우 exception을 발생시킴.
Listing 3-2)
NSArray *moreTransactions = @[ /* transaction data */ ];
NSArray *arrayOfArrays = @[ [self transactions], moreTransactions ];
@distinctUnionOfArrays
: 합집합, 중복제거
NSArray *collectedDistinctPayees = [arrayOfArrays valueForKeyPath:@"@distinctUnionArrays.payee"];
@unionOfArrays
: 합집합
NSArray *collectedPayees = [arrayOfArrays valueForKeyPath:@"@unionOfArrays.payee"];
@distinctUnionOfSets
: distinctUnionOfArrays의 NSSet버전.
: 중복 NSSet에 대해서 동작함.
4. Representing Non-Object Values
KVO의 getter를 통해 얻어오려는 값이 non-object인 경우 NSNumber(for scalars) 또는 NSValue(for structures - CGPoint, NSRange, CGRect, CGSize)로 변환해서 return한다.
비슷한 논리로 setter로 세팅하려는 property가 non-object인 경우, 넘어오는 파라미터에 "<type>Value" 메서드를 호출해서 property에 맞는 타입의 값을 세팅한다.
non-object의 property에 KVO setter값에 nil을 넣으면 setNilValueForKey: 메서드가 불린다. 이 메서드의 default동작은 NSInvalidArgumentException을 발생시킨다. Custom처리를 하려면 해당 메서드를 오버라이드 해야 한다.
Wrapping and Unwrapping Structures
CGPoint, NSRange, CGRect, CGSize이외에 사용자가 정의한 Structure도 가능하다.
Listing 4-1) A sample class using a custom structure
typedef struct
{
float x, y, z;
} ThreeFloats
@interface MyClass
@property (nonatomic) ThreeFloats threeFloats;
@end
-> wrapping, unwrapping
NSValue *result = [myClass valueForKey:@"threeFloats"];
// NSValue로 자동 wrapping 된다.
ThreeFloats floats = { 1.0, 2.0, 3.0 };
NSValue *value = [NSValue valueWithBytes:&floats objCType:@encode(ThreeFloats)];
[myClass setValue:value forKey:@"threeFloats"];
// 해당 property로 setting될때는 NSValue의 getValue: 메서드를 이용해 자동 unwrapping되서 저장된다.
5.Validating Properties
- 참조: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding/ValidatingProperties.html#//apple_ref/doc/uid/10000107i-CH18-SW1
6. Accessor Search Patterns
- 참조: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/KeyValueCoding/SearchImplementation.html#//apple_ref/doc/uid/20000955-CJBBBFFA
* Performance?
: 애플은 KVC가 indirect level에서의 접근이기 때문에 direct method invocation(직접 method call)보다 flexiblity를 제공하지만 속도측면에서는 약간 느리다고 밝히고 있다. 그래서 KVC가 제공하는 이득을 볼 수 있을때만 사용하라고 권장하고 있다. 하지만 둘간에 performance차이는 아주 경미한 수준이라 performance에 민감한 프로그램이 아니라면 무시해도 괜찮을 것 같다.
'IOS > Objective-C' 카테고리의 다른 글
GCD(Grand Central Dispatch)(1) (0) | 2016.12.19 |
---|---|
Objective-C의 동적 바인딩, 그리고 Message dispather와 Runtime (0) | 2016.12.18 |
Collections의 weak reference (0) | 2016.10.02 |
NSString과 NSMutableString의 copy (0) | 2016.02.18 |
멀티 스레드 프로그램 (0) | 2015.08.23 |