Access to the collection through KVC (to protect the collection and comply with KVO requirements)

I have a Test class that has an array of Foos. I want to provide access to Foos without directly exposing ivar. I am trying to make this KVC compatible (also paving the way for KVO compliance). I have:

test.h

@interface Test : NSObject { NSMutableArray *foos; } @property (readonly, copy) NSMutableArray *foos; @end 

Test.m

 - (id) init { self = [super init]; if (self != nil) { foos = [[NSMutableArray array] retain]; } return self; } - (NSMutableArray*) foos { return [self mutableArrayValueForKey:@"foos"]; } - (NSUInteger)countOfFoos { return [foos count]; } - (id)objectInFoosAtIndex:(NSUInteger)index { return [foos objectAtIndex:index]; } - (NSArray *)foosAtIndexes:(NSIndexSet *)indexes { return [foos objectsAtIndexes:indexes]; } - (void)insertObject:(id)key inFoosAtIndex:(NSUInteger)index { [foos insertObject:key atIndex:index]; } - (void)insertFoos:(NSArray *)foosArray atIndexes:(NSIndexSet *)indexes { [foos insertObjects:foosArray atIndexes:indexes]; } - (void)removeObjectFromFoosAtIndex:(NSUInteger)index { [foos removeObjectAtIndex:index]; } - (void)removeFoosAtIndexes:(NSIndexSet *)indexes { [foos removeObjectsAtIndexes:indexes]; } 

This leads to an endless loop when the client tries to add Foo:

 Test *test = [[Test alloc] init]; NSMutableArray *foos = test.foos; [foos addObject:@"adding object"]; // infinite loop here 

What am I doing wrong?

+4
source share
3 answers
 - (NSMutableArray*) foos { return [self mutableArrayValueForKey:@"foos"]; } 

The accessory should not use KVC to get the value of an available property; the idea is that KVC goes through accessors because accessors are closer to value than KVC.

A proper foos implementation should return a copy, modifiable or otherwise, of the array. Here's how I do it:

 - (NSArray *) foos { return [[foos copy] autorelease]; } 

I would also make all accessors available. Anything that wants to mutate an array or randomly access elements at specific indices can do so. It is still safe and encapsulated, because it goes through your accessories and not directly accesses the array.

There really is no reason to use the KVC protocol methods yourself if you donโ€™t know which key you will have access to at the time of writing. For example, if you are writing a nib loader or Cocoa Bindings system, you should use KVC.

+2
source

The problem is that the NSMutableArray proxy returned by mutableArrayValueForKey: must first get the real array that it executes with the foos method. Since the one that returns the NSMutableArray proxy goes into an infinite loop. One solution is to use a different name:

 - (NSMutableArray*) mutableFoos { return [self mutableArrayValueForKey:@"foos"]; } 
+1
source

I spent a lot of time on this problem and wanted to get it through an accessory. I wanted to clarify in the answer for those who came. This is what I did:

 @property (nonatomic,readonly,getter=getTheFoos) NSMutableArray* foos; 

Then, obviously, it is implemented:

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

If you were careful, getFoos appears to be an (undocumented) KVC accessory because it sends it in the same loop.

Then on KVO:

 Test* test= [[Test alloc] init]; NSObject* obj= [[NSObject alloc] init]; NSMutableArray* arrTheData= test.foos; [test.foos insertObject:obj atIndex:0]; [arrFoos insertObject:obj atIndex:0]; 

arrFoos can read the updated mutated array (there will be two objects in it), but inserting into it will not start KVO. Somewhere in my adventures, I saw that returning from mutableArrayValueForKey: does not return NSMutableArray *, but a subclass of this, which may be the reason for this.

0
source

Source: https://habr.com/ru/post/1300765/


All Articles