수색…


가장 일반적인 실제 생활 키 값 코딩 예

키 값 코딩은 NSKeyValueCoding 프로토콜을 사용하여 NSObject에 통합됩니다.

이게 무슨 뜻이야?

이는 모든 id 객체가 valueForKey 메소드와 valueForKeyPath 등 다양한 변형을 호출 할 수 있음을 의미합니다. '

또한 모든 id 객체가 setValue 메소드와 다양한 변형을 호출 할 수 있음을 의미합니다.

예:

id obj = [[MyClass alloc] init];
id value = [obj valueForKey:@"myNumber"];

int myNumberAsInt = [value intValue];
myNumberAsInt = 53;
[obj setValue:@(myNumberAsInt) forKey:@"myNumber"];

예외 :

위의 예에서는 MyClass에 myNumber라는 NSNumber 속성이 있다고 가정합니다. myNumber가 MyClass 인터페이스 정의에 나타나지 않으면 NSUndefinedKeyException을 2 행 및 5 행 모두에서 발생시킬 수 있습니다 - 일반적으로 다음과 같이 알려져 있습니다.

this class is not key value coding-compliant for the key myNumber.

왜 이렇게 강력합니까?

클래스의 인터페이스 없이도 클래스의 속성에 동적으로 액세스 할 수있는 코드를 작성할 수 있습니다. 즉, NSObject에서 파생 된 객체의 속성에서 값을 표시 할 수 있습니다. 단, 속성 이름은 런타임에 동적으로 제공되어야합니다.

위의 예제에서 코드는 MyClass가 사용 가능하지 않고 호출 코드에서 사용할 수있는 id 유형 obj 없이도 작동 할 수 있습니다.

주요 가치 관찰

핵심 가치 관찰 설정.

이 경우 관찰자가 소유하고있는 객체의 contentOffset 을 관찰하고 싶습니다.

//
// Class to observe
//
@interface XYZScrollView: NSObject
@property (nonatomic, assign) CGPoint contentOffset;
@end

@implementation XYZScrollView
@end


//
// Class that will observe changes
//
@interface XYZObserver: NSObject
@property (nonatomic, strong) XYZScrollView *scrollView;
@end

@implementation XYZObserver

// simple way to create a KVO context
static void *XYZObserverContext = &XYZObserverContext;


// Helper method to add self as an observer to 
// the scrollView's contentOffset property
- (void)addObserver {

    // NSKeyValueObservingOptions
    //
    // - NSKeyValueObservingOptionNew
    // - NSKeyValueObservingOptionOld
    // - NSKeyValueObservingOptionInitial
    // - NSKeyValueObservingOptionPrior
    //
    // can be combined:
    // (NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld)
    
    NSString *keyPath = NSStringFromSelector(@selector(contentOffset));
    NSKeyValueObservingOptions options = NSKeyValueObservingOptionNew;    

    [self.scrollView addObserver: self 
                      forKeyPath: keyPath 
                         options: options
                         context: XYZObserverContext];
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSString *,id> *)change context:(void *)context {
    
    if (context == XYZObserverContext) { // check the context

        // check the keyPath to see if it's any of the desired keyPath's.
        // You can observe multiple keyPath's
        if ([keyPath isEqualToString: NSStringFromSelector(@selector(contentOffset))]) {

            // change dictionary keys:
            // - NSKeyValueChangeKindKey
            // - NSKeyValueChangeNewKey
            // - NSKeyValueChangeOldKey
            // - NSKeyValueChangeIndexesKey
            // - NSKeyValueChangeNotificationIsPriorKey
            
            // the change dictionary here for a CGPoint observation will
            // return an NSPoint, so we can take the CGPointValue of it.
            CGPoint point = [change[NSKeyValueChangeNewKey] CGPointValue];
            
            // handle point
        }
        
    } else {

        // if the context doesn't match our current object's context
        // we want to pass the observation parameters to super
        [super observeValueForKeyPath: keyPath
                             ofObject: object
                               change: change
                              context: context];
    }
}

// The program can crash if an object is not removed as observer 
// before it is dealloc'd
//
// Helper method to remove self as an observer of the scrollView's
// contentOffset property
- (void)removeObserver {
    NSString *keyPath = NSStringFromSelector(@selector(contentOffset));
    [self.scrollView removeObserver: self forKeyPath: keyPath];
}

@end

KVC 데이터 쿼리

if ([[dataObject objectForKey:@"yourVariable"] isEqualToString:"Hello World"]) {
    return YES;
} else {
    return NO;
}

KVC를 사용하여 저장된 값을 로컬 변수로 검색하거나 캐스팅하지 않고도 빠르고 쉽게 쿼리 할 수 ​​있습니다.

콜렉션 연산자

Collection Operators 는 KVC 키 경로에서 "컬렉션 유형"속성 (즉, NSArray , NSSet 및 유사한)에 대한 작업을 수행하는 데 사용될 수 있습니다. 예를 들어, 수행 할 공통 작업은 컬렉션의 개체를 계산하는 것입니다. 이를 위해 @count 콜렉션 연산자 를 사용합니다 :

self.array = @[@5, @4, @3, @2, @1];
NSNumber *count = [self.array valueForKeyPath:@"@count"];
NSNumber *countAlt = [self valueForKeyPath:@"array.@count"];
// count == countAlt == 5

이것은 여기에서 완전히 중복되지만 ( count 속성에 액세스했을 수도 있음), 거의 필요하지는 않지만 때때로 유용 할 있습니다. 있다, 그러나, 훨씬 더 유용, 즉 일부 수집 사업자 @max , @min , @sum , @avg@unionOf 가족. 이들 사업자는 제대로 작동하려면 연산자 뒤에 별도의 키 경로를 필요로하는 것이 중요하다. 다음은 이들의 목록과 함께 작동하는 데이터 유형입니다.

운영자 데이터 형식
@count (없음)
@max NSNumber , NSDate , int (및 관련) 등
@min NSNumber , NSDate , int (및 관련) 등
@sum NSNumber , int (및 관련), double (및 관련) 등
@avg NSNumber , int (및 관련), double (및 관련) 등
@unionOfObjects NSArray , NSSet
@distinctUnionOfObjects NSArray , NSSet
@unionOfArrays NSArray<NSArray*>
@distinctUnionOfArrays NSArray<NSArray*>
@distinctUnionOfSets NSSet<NSSet*>

@max@min 은 컬렉션에있는 객체의 속성 중 가장 높은 값 또는 가장 낮은 값을 각각 반환합니다. 예를 들어 다음 코드를 살펴보십시오.

// “Point” class used in our collection
@interface Point : NSObject

@property NSInteger x, y;

+ (instancetype)pointWithX:(NSInteger)x y:(NSInteger)y;

@end

...

self.points = @[[Point pointWithX:0 y:0],
                [Point pointWithX:1 y:-1],
                [Point pointWithX:5 y:-6],
                [Point pointWithX:3 y:0],
                [Point pointWithX:8 y:-4],
];

NSNumber *maxX = [self valueForKeyPath:@"[email protected]"];
NSNumber *minX = [self valueForKeyPath:@"[email protected]"];
NSNumber *maxY = [self valueForKeyPath:@"[email protected]"];
NSNumber *minY = [self valueForKeyPath:@"[email protected]"];

NSArray<NSNumber*> *boundsOfAllPoints = @[maxX, minX, maxY, minY];

...

Key-Value Coding 콜렉션 연산자의 힘으로 4 줄의 코드와 순수한 Foundation에서 배열의 모든 점을 캡슐화하는 사각형을 추출 할 수있었습니다.

이러한 비교는 객체에 대해 compare: 메소드를 호출하여 이루어 지므로 자신의 클래스를이 연산자와 호환되도록하려면이 메소드를 구현해야합니다.

@sum 은 아마 추측 할 수 있듯이 속성의 모든 값을 합산합니다.

@interface Expense : NSObject

@property NSNumber *price;

+ (instancetype)expenseWithPrice:(NSNumber *)price;

@end

...

self.expenses = @[[Expense expenseWithPrice:@1.50],
                  [Expense expenseWithPrice:@9.99],
                  [Expense expenseWithPrice:@2.78],
                  [Expense expenseWithPrice:@9.99],
                  [Expense expenseWithPrice:@24.95]
];

NSNumber *totalExpenses = [self valueForKeyPath:@"[email protected]"];

여기서 @sum 을 사용하여 배열의 모든 비용의 총 가격을 찾습니다. 우리가 대신 각 비용에 대해 지불하는 평균 가격을 찾고 싶다면 @avg 를 사용할 @avg .

NSNumber *averagePrice = [self valueForKeyPath:@"[email protected]"];

마지막으로, @unionOf 제품군이 있습니다. 이 가족에는 5 개의 서로 다른 연산자가 있지만, 각각은 서로 거의 차이가 없지만 거의 동일하게 작동합니다. 먼저 @unionOfObjects 가 있습니다.이 배열은 배열에있는 객체의 속성 배열을 반환합니다.

// See "expenses" array above

NSArray<NSNumber*> *allPrices = [self valueForKeyPath:
    @"[email protected]"];

// Equal to @[ @1.50, @9.99, @2.78, @9.99, @24.95 ]

@distinctUnionOfObjects@unionOfObjects 와 동일하게 @unionOfObjects 하지만 중복을 제거합니다.

NSArray<NSNumber*> *differentPrices = [self valueForKeyPath:
    @"[email protected]"];

// Equal to @[ @1.50, @9.99, @2.78, @24.95 ]

마지막으로 @unionOf 패밀리의 마지막 3 개 연산자가 한 단계 더 깊숙이 들어가서 이중 중첩 배열에 포함 된 속성에 대해 발견 된 값의 배열을 반환합니다.

NSArray<NSArray<Expense*,Expense*>*> *arrayOfArrays =
    @[
        @[ [Expense expenseWithPrice:@19.99],
           [Expense expenseWithPrice:@14.95],
           [Expense expenseWithPrice:@4.50],
           [Expense expenseWithPrice:@19.99]
         ],

        @[ [Expense expenseWithPrice:@3.75],
           [Expense expenseWithPrice:@14.95]
         ]
     ];

// @unionOfArrays
NSArray<NSNumber*> allPrices = [arrayOfArrays valueForKeyPath:
    @"@unionOfArrays.price"];
// Equal to @[ @19.99, @14.95, @4.50, @19.99, @3.75, @14.95 ];

// @distinctUnionOfArrays
NSArray<NSNumber*> allPrices = [arrayOfArrays valueForKeyPath:
    @"@distinctUnionOfArrays.price"];
// Equal to @[ @19.99, @14.95, @4.50, @3.75 ];

이 예제에서 누락 된 것은 @distinctUnionOfSets , 이것은 @distinctUnionOfArrays 와 똑같은 기능을합니다. 대신 NSSet 사용하고 리턴합니다. (세트에서 모든 오브젝트가 구별되어야하므로 별다른 distinct 이 없습니다.)

그리고 그게 다야! 콜렉션 운영자는 올바르게 사용된다면 정말 강력 할 수 있으며, 불필요하게 루프를 돌지 않아도됩니다.

마지막으로 주목할 점은 추가 속성 접근없이 NSNumber 의 배열에서 표준 컬렉션 연산자를 사용할 수도 있다는 것입니다. 이렇게하려면 객체를 반환하는 self 의사 속성에 액세스합니다.

NSArray<NSNumber*> *numbers = @[@0, @1, @5, @27, @1337, @2048];

NSNumber *largest = [numbers valueForKeyPath:@"@max.self"];
NSNumber *smallest = [numbers valueForKeyPath:@"@min.self"];
NSNumber *total = [numbers valueForKeyPath:@"@sum.self"];
NSNumber *average = [numbers valueForKeyPath:@"@avg.self"];


Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow