Recherche…


Exemple le plus commun de codage de valeur réelle

Key Value Coding est intégré à NSObject en utilisant le protocole NSKeyValueCoding .

Qu'est-ce que cela signifie?

Cela signifie que tout objet id est capable d'appeler la méthode valueForKey et ses différentes variantes comme valueForKeyPath, etc.

Cela signifie également que tout objet id peut invoquer la méthode setValue et ses différentes variantes.

Exemple:

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

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

Des exceptions:

L'exemple ci-dessus suppose que MyClass possède une propriété NSNumber appelée myNumber. Si myNumber n'apparaît pas dans la définition de l'interface MyClass, une exception NSUndefinedKeyException peut être déclenchée sur les deux lignes 2 et 5 - communément appelée:

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

Pourquoi c'est si puissant:

Vous pouvez écrire du code qui peut accéder dynamiquement aux propriétés d'une classe, sans avoir besoin d'interface pour cette classe. Cela signifie qu'une vue de table peut afficher les valeurs de toutes les propriétés d'un objet dérivé de NSObject, à condition que ses noms de propriété soient fournis dynamiquement à l'exécution.

Dans l'exemple ci-dessus, le code peut aussi bien fonctionner sans que MyClass soit disponible et que ID type obj soit disponible pour le code d'appel.

Observation de la valeur clé

Mettre en place l'observation de la valeur clé.

Dans ce cas, nous voulons observer le contentOffset sur un objet que notre observateur possède

//
// 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

Interrogation des données KVC

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

Vous pouvez interroger les valeurs stockées à l'aide de KVC rapidement et facilement, sans devoir les extraire ou les convertir en variables locales.

Opérateurs de collecte

Les opérateurs de collecte peuvent être utilisés dans un chemin de clé KVC pour effectuer une opération sur une propriété de type «collection-type» ( NSArray , NSSet et similaire). Par exemple, une opération courante consiste à compter les objets dans une collection. Pour ce faire, vous utilisez l' opérateur de collecte @count :

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

Bien que cela soit complètement redondant ici (nous aurions pu simplement accéder à la propriété count ), cela peut être utile à l'occasion, bien que cela soit rarement nécessaire. Il existe cependant des opérateurs de collecte beaucoup plus utiles, à savoir @max , @min , @sum , @avg et la famille @unionOf . Il est important de noter que ces opérateurs requièrent également un chemin de clé séparé suivant l'opérateur pour fonctionner correctement. Voici une liste de ceux-ci et le type de données avec lesquelles ils travaillent:

Opérateur Type de données
@count (aucun)
@max NSNumber , NSDate , int (et apparenté), etc.
@min NSNumber , NSDate , int (et apparenté), etc.
@sum NSNumber , int (et apparenté), double (et connexe), etc.
@avg NSNumber , int (et apparenté), double (et connexe), etc.
@unionOfObjects NSArray , NSSet , etc.
@distinctUnionOfObjects NSArray , NSSet , etc.
@unionOfArrays NSArray<NSArray*>
@distinctUnionOfArrays NSArray<NSArray*>
@distinctUnionOfSets NSSet<NSSet*>

@max et @min respectivement la valeur la plus élevée ou la plus basse d'une propriété des objets de la collection. Par exemple, regardez le code suivant:

// “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];

...

Avec seulement 4 lignes de code et une base pure, avec la puissance des opérateurs de collection Key-Value Coding, nous avons pu extraire un rectangle qui encapsule tous les points de notre tableau.

Il est important de noter que ces comparaisons sont effectuées en invoquant la méthode compare: sur les objets. Par conséquent, si vous souhaitez rendre votre propre classe compatible avec ces opérateurs, vous devez implémenter cette méthode.

@sum vous pouvez probablement le deviner, @sum ajoute toutes les valeurs d'une propriété.

@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]"];

Ici, nous avons utilisé @sum pour trouver le prix total de toutes les dépenses dans le tableau. Si nous voulons plutôt trouver le prix moyen que nous payons pour chaque dépense, nous pouvons utiliser @avg :

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

Enfin, il y a la famille @unionOf . Il y a cinq opérateurs différents dans cette famille, mais ils fonctionnent tous essentiellement de la même façon, avec seulement de petites différences entre eux. Tout d'abord, il y a @unionOfObjects qui renverra un tableau des propriétés des objets d'un tableau:

// See "expenses" array above

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

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

@distinctUnionOfObjects fonctionne de la même manière que @unionOfObjects , mais il supprime les doublons:

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

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

Et enfin, les trois derniers opérateurs de la famille @unionOf vont aller plus loin et renvoient un tableau de valeurs trouvées pour une propriété contenue dans des tableaux imbriqués en double:

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 ];

Celui qui manque dans cet exemple est @distinctUnionOfSets , mais cela fonctionne exactement comme @distinctUnionOfArrays , mais fonctionne avec et renvoie NSSet s à la place (il n'y a pas de version non distinct car dans un ensemble, chaque objet doit être distinct de toute façon).

Et c'est tout! Les opérateurs de collecte peuvent être très puissants s'ils sont utilisés correctement et peuvent éviter d'avoir à parcourir des fichiers inutilement.

Une dernière remarque: vous pouvez également utiliser les opérateurs de collecte standard sur les tableaux de NSNumber s (sans accès aux propriétés supplémentaires). Pour ce faire, vous accédez à la pseudo-propriété self qui renvoie simplement l'objet:

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
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow