7 votes

Observation du comptage dans NSMutableArray

J'aimerais être informé lorsque le nombre d'éléments d'un tableau NSA change Bien sûr, je n'en aurais pas besoin si je contrôlais l'ajout et la suppression des objets dans le tableau. Mais ce n'est pas le cas, cela se produit de manière imprévisible en fonction du modèle de processus d'entreprise et dépend de facteurs externes. Existe-t-il une solution simple et élégante ?

EDIT : Je corrige cela en NSMutableArray bien sûr

15voto

CodaFi Points 29073

Vous devrez utiliser KVC . Mais comment s'y prendre ? Après tout, NSMutableArray n'est pas compatible avec le Key-Value-Coding pour ses méthodes de mutation ou les changements de contenu. La réponse est la procuration, car la sous-classification de NS[Mutable]Array est beaucoup trop compliquée.

NSProxy est une petite classe géniale que vous pouvez utiliser pour intercepter les messages envoyés à votre tableau comme si vous étiez un NSMutableArray, puis les transmettre à une instance interne. Malheureusement, elle n'est pas non plus compatible avec KVC, car les rouages de KVC se trouvent dans NSObject. Nous devrons donc l'utiliser. Un exemple d'interface pourrait ressembler à ceci :

@interface CFIKVCMutableArrayProxy : NSObject  {
    NSMutableArray *_innerArray;
}

- (NSUInteger)count;

- (void)insertObject:(id)anObject atIndex:(NSUInteger)index;
- (void)removeObjectAtIndex:(NSUInteger)index;
- (void)addObject:(id)anObject;
- (void)removeLastObject;
- (void)insertObjects:(NSArray *)objects atIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectAtIndex:(NSUInteger)index withObject:(id)anObject;

//…

@end

Comme vous pouvez le voir, nous simulons une interface de NSMutableArray ce qui est nécessaire, car notre proxy doit tout mettre en œuvre comme s'il était un NSMutableArray . Cela rend également l'implémentation aussi simple que possible, puisque nous pouvons simplement transmettre les sélecteurs à notre module interne NSMutableArray pointeur. Pour des raisons de brièveté, je n'implémenterai que deux méthodes pour vous montrer à quoi ressemble un schéma général :

@implementation CFIKVCMutableArrayProxy

//…

- (NSUInteger)count {
    return _innerArray.count;
}

- (void)addObject:(id)anObject {
    [self willChangeValueForKey:@"count"];
    [_innerArray addObject:anObject];
    [self didChangeValueForKey:@"count"];
}

- (void)removeLastObject {
    [self willChangeValueForKey:@"count"];
    [_innerArray removeLastObject];
    [self didChangeValueForKey:@"count"];
}

@end

Si vous n'avez pas la possibilité d'envelopper un tableau de cette façon, alors essayez de repenser votre code. Si une dépendance externe vous contraint à ce genre de situation, essayez de la supprimer. C'est toujours une mauvaise chose de travailler autour de ses propres outils.

6voto

malex Points 2992

Pour observer les changements dans un mutableArray, on doit utiliser un objet proxy mutable donné par

 - (NSMutableArray *)mutableArrayValueForKey:(NSString *)key

qui est conforme à la norme KVO, c'est-à-dire que tout changement d'objet proxy envoie des notifications de changement de volonté.

La classe de démonstration suivante montre l'implémentation complète

@interface DemoClass : NSObject

@property (nonatomic) NSMutableArray *items;

- (void)addItemsObserver:(id)object;
- (void)removeItemsObserver:(id)object;

@end

@implementation DemoClass

- (NSMutableArray *)items;
{
    return [self mutableArrayValueForKey:@"_items"];
}

- (void)addItemsObserver:(id)object
{
    [self addObserver:object forKeyPath:@"_items.@count" options:NSKeyValueObservingOptionOld|NSKeyValueObservingOptionNew context:nil];
}

- (void)removeItemsObserver:(id)object
{
    [self removeObserver:object forKeyPath:@"_items.@count" context:nil];
}
@end

@interface ObservingClass : NSObject

@property (nonatomic) DemoClass *demoObject;

@end

@implementation ObservingClass

- (instanstype)init
{
     if (self = [super init]) {
         _demoObject = [DemoClass new];

         [_demoObject addItemsObserver:self];
     }
     return self;
}

- (void)observeValueForKeyPath:(NSString *)keyPath
                  ofObject:(id)object
                    change:(NSDictionary *)change
                   context:(void *)context
{
     NSLog(@"is called on demoObject.items.count change");
}

- (void)dealloc
{
    [_demoObject removeItemsObserver:self];
}

@end

Maintenant, à chaque fois que vous ajoutez ou supprimez un objet dans la base de données items vous verrez un nouveau log dans la console ( observeValueForKeyPath est appelé).

Tout changement direct d'ivar auto-synthétisé _items n'aura aucun effet.

Notez également que vous avez fortement besoin de définir l'observateur sur _items.@count (observant items.@count est insensé).

Notez que vous n'avez pas besoin d'init. _items o self.items . Cela se fera en coulisse lorsque vous appellerez. items getter.

Chaque fois que vous changez le "tableau" items vous obtiendrez un nouvel objet _items avec une nouvelle adresse. Mais je peux toujours le trouver via items proxy getter.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X