Re: Implementing NSFastEnumeration

Christiaan Hofman <[email protected]>
Newsgroups gmane.comp.macosx.devel
Message-ID <[email protected]>
On Oct 1, 2009, at 17:19, Gerd Knops wrote:

>
> On Oct 1, 2009, at 8:13 AM, Christiaan Hofman wrote:
>
>> I've been reading the documentation on NSFastEnumeration (both the  
>> docs on the protocol and the section in the Objective-C 2.0  
>> Programming Language guide). But it is really cryptic and misses  
>> most of the relevant information to help in implementing it.
>
> Below some code I have been using. To access the array to be  
> enumerated, it uses these methods that my code provides:
>
> - (unsigned)version;
> - (unsigned)countOfChildrenAtVersion:(unsigned)aVersion;
> - (id)childAtIndex:(unsigned)index forVersion:(unsigned)aVersion;
>
> It makes use of the extra array to keep track of the version it is  
> enumerating, in case it changes while the enumeration goes on. In my  
> case it is safe to use "self" as the mutationsPtr because the only  
> way the child array might change is by adding additional children.
>
> Looking at it now I am not sure if the [child retain] part is  
> correct, might create a memory leak. I'll have to check on that. And  
> it'll need some changes to be 64-bit safe.
>
> But anyway this should get you started.
>
> Gerd
>
> - (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *) 
> state
> 	objects:(id *)stackbuf
> 	count:(NSUInteger)len
> {
> 	
> 	// typedef struct {
> 	//    unsigned long state;
> 	//    id *itemsPtr;
> 	//    unsigned long *mutationsPtr;
> 	//    unsigned long extra[5];
> 	// } NSFastEnumerationState;
> 	
> 	unsigned	idx=state->state;
> 	unsigned	version=(idx>0)?state->extra[0]:[self version];
> 	unsigned	numChildren=[self countOfChildrenAtVersion:version];
> 	NSUInteger	now=0;
> 	
> 	while(now<len && idx<numChildren)
> 	{
> 		id	child=[self childAtIndex:idx forVersion:version];
> 		
> 		stackbuf[now++]=[child retain];
> 		idx++;
> 	}
> 	
> 	state->extra[0]=version;
> 	state->state=(unsigned long)idx;
> 	state->itemsPtr=stackbuf;
> 	state->mutationsPtr=(unsigned long *)self;
> 	
> 	return now;
> }
>

First of all, thanks for those who replied. From the replies (and also  
a test I just did) it seems the docs are simply wrong, so I filed a  
radar against the docs (also asking for real docs instead of the  
current useless mess).

One problem for me is that the actually returned objects are temporary  
autoreleased objects. I'm sure your retain is wrong without GC. The  
other thing is that I want to enumerate the underlying array of keys  
first. I tried something along these lines:

- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *) 
state
	objects:(id *)stackbuf
	count:(NSUInteger)len
{
	NSUInteger i, count = [keys countByEnumeratingWithState:state  
objects:stackbuf count:len];
	for (i = 0; i < count; i++)
	{
		id key = state->itemsPtr[i];
		id value = [values objectForKey:key];
		stackbuf[i] = value;
	}
	state->itemsPtr = stackbuf;
	return count;
}

While this works for small arrays, for larger arrays this gives a  
memory access crasher. The reason seems to be that NSArray can return  
more items than len (the size of stackbuf) in a single call.

Would there be a way to modify this implementation to work? Or am I  
forced to get the objects from keys without using fast enumeration (in  
which case fast enumeration becomes hardly fast)?

And another question I have been unable to find an answer to in the  
docs. Foundation collections are implemented as class clusters. So is  
NSFastEnumeration implemented as a primitive or a derived method? In  
other words: when I subclass NSArray (or another collection class),  
does it already have a default implementation of NSFastEnumeration, or  
am I forced to always implement it when I want to use for(...in...)?

thanks,
Christiaan
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.