How to write your own iterator for a property collection property (with the correct type)?

I have a model class with some composition of objects, and I don’t know how best to write iterators for this. To see the problem in more detail, here is the hierarchy (semi-pseudo-code):

Root Class:

MYEntity : NSObject @property int commonProperty; @property NSArray *childs; //Childs of any kind. 

Some specific subclasses:

 MYConcreteStuff : MYEntity @property int number; MYConcreteThing : MYEntity @property NSString *string; 

And the root object with specific collections:

 MYRoot : MYEntity @property MYEntity *stuff; //Collect only stuff childs here. @property MYEntity *things; //Collect only thing childs here. 

Now I can write cool access controls for collections (in MYEntity), for example:

 -(MYEntity*)entityForIndex:(int) index { if ([self.childs count] > index) return [self.childs objectAtIndex:index]; return nil; } 

And even more cool, cool cast accessors for the root object.

 -(MYConcreteThing*)thingForIndex:(int) index { if ([self.things count] > index) return (MYConcreteThing*)[self.things entityForIndex]; return nil; } 

But I have no idea how to write some oneliner iterators for such collections. The wannabe client code looks something like this:

 for (MYConcreteThing *eachThing in myRoot.things) eachThing.string = @"Success."; //Set "thingy" stuff thanks to the correct type. 

I am thinking about using blocks, but there might be a cleaner solution. Any ideas / experience?

+8
iterator collections casting ios objective-c
source share
2 answers

Now I will continue the lock, it is quite simple. Now I prefer the term to list.

Block type for listing things (ensuring the correct type):

 typedef void (^MYThingEnumeratorBlock)(MYThing *eachThing); 

A cool enumerator method for Things in MYRoot (not to expose collections):

 -(void)enumerateThings:(MYThingEnumeratorBlock) block { for (MYThing *eachThing in self.things.childs) block(eachThing); } 

So, the client code goes:

 [myRoot enumerateThings:^(MYThing *eachThing) { NSLog(@"Thing: %@", eachThing.string); }]; 

Using a neat macro:

 #define ENUMARATE_THINGS myRoot enumerateThings:^(MYThing *eachThing) [ENUMARATE_THINGS { NSLog(@"Thing: %@", eachThing.string); //Cool "thingy" properties. }]; 
+8
source share

The best way to do this, in my opinion, would be to implement methods compatible with key values ​​for the properties of the array. This would add a bonus to allowing your collections to be watched by other objects. You can read all about this in the apple documentation. The following is an example implementation of the Things array in the MYRoot class. Feel free to personalize the code in each method:

 // KVC method for read-only array of Things - (NSUInteger) countOfThings { return _things.count; } - (Thing*) objectInThingsAtIndex:(NSUInteger)index { return [_things objectAtIndex:index]; } // Additional KVC methods for mutable array collection - (void) insertObject:(Thing*)thing inThingsAtIndex:(NSUInteger)index { [_things insertObject:thing atIndex:index]; } - (void) removeObjectInThingsAtIndex:(NSUInteger)index { [_things removeObjectAtIndex:index]; } 

To iterate over a collection, you must do the following:

 for (Thing *thing in [_entity valueForKey:@"things"]) { } 

To add Thing to an array you could do

 NSMutableArray *children = [_entity mutableArrayValueForKey:@"things"]; [children addObject:aThing]; 

This way you guarantee that the entire object observing the @"things" property will be notified of all changes in the array. If you call the insertion methods directly, they will not (this is sometimes useful as you see fit).

+1
source share

All Articles