Поиск…


Наиболее распространенный пример кодирования ключевых значений реальной жизни

Key Coding Value интегрирован в NSObject с использованием протокола NSKeyValueCoding.

Что это значит?

Это означает, что любой объект 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 имеет свойство NSNumber, называемое myNumber. Если 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, без необходимости извлекать их или использовать в качестве локальных переменных.

Операторы коллекции

Операторы коллекции могут использоваться в ключевом ключе KVC для выполнения операции над свойством «тип коллекции» (например, NSArray , NSSet и т. NSArray ). Например, общая операция для выполнения - это подсчет объектов в коллекции. Для этого вы используете оператор коллекции @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];

...

Всего в 4 строках кода и в чистом фонде с мощью операторов коллекции Key-Value Coding мы смогли извлечь прямоугольник, который инкапсулирует все точки в нашем массиве.

Важно отметить, что эти сравнения выполняются путем вызова метода 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 :

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

Наконец, есть семья @unionOf . В этом семействе существует пять разных операторов, но все они работают в основном одинаково, с небольшими различиями между ними. Во-первых, есть @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 , но удаляет дубликаты:

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

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

И, наконец, последние 3 оператора в семье @unionOf пройдут на один шаг глубже и вернут массив значений, найденных для свойства, содержащегося внутри двоично вложенных массивов:

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 s вместо (нет не- distinct версий , поскольку в наборе, каждый объект должен быть отличным в любом случае).

И это все! Операторы коллекции могут быть очень мощными при правильном использовании и могут помочь избежать необходимости излишнего циклического перемещения файлов.

Последнее замечание: вы также можете использовать стандартные операторы коллекции в массивах NSNumber s (без дополнительного доступа к свойствам). Для этого вы получаете доступ к 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