Általános leírás
Ahogyan a vezérlési szerkezete foreach szekciójában már láthattuk, lehetőségünk van végigiterálni egy kollekció elemein
(iOS 2.0 és későbbi verziókban).
for ( Item* item in myCollection ) { /*...*/ }
Erre az NSArray, az NSDictionary, valamint legtöbb Foundation Frameworkben definiált gyűjteményben lehetőségünk van.
Ha azt szeretnénk, hogy a saját osztályunkon is lehessen ilyen módon iterálni, akkor meg kell felelnünk az NSFastEnumeration
protokollnak.
NSFastEnumeration protokoll
Az NSFastEnumeration protokollt a NSEnumerator.h fájlban deklarálja a Foundation Framework az alábbi módon:
@protocol NSFastEnumeration
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
@end
A paraméterek jelentése:
- state - az iteráció állapotára vonatkozó információk, többek között annak biztosítására, hogy a gyűjtemény
az iteráció során nem változott meg
- stackbuf - objektumok C tömbje, amin a hívó iterálhat.
- len - az objektumok maximális száma, ami a stackbuf-ba írható
- visszatérési érték - a stackbuf-ba írt objektumok száma. Nem haladhatja meg a len paramétert. Az iteráció végét
az jelzi, ha a visszatérési érték nulla.
NSFastEnumerationState
A fentebb látható iteráció során ilyen típusú objektumban tárolhatjuk a szükséges információkat. Az NSFastEnumerationState az
alábbi módon van definiálva:
typedef struct {
unsigned long state;
id *itemsPtr;
unsigned long *mutationsPtr;
unsigned long extra[5];
} NSFastEnumerationState;
Mezők jelentése:
- state - önkényes információ az iteráció állapotáról. Tipikusan az iteráció elején nulla.
- itemsPtr - objektumok C tömbje, amin a hívó iterálhat. Nem szükséges megegyeznie a countByEnumeratingWithState:objects:count:
függvény meghívása során kapott stackbuf tömbbel, ilyenkor a len értékét is figyelmen kívül lehet hagyni, mert
az iteráció az itemsPtr tömbön történik.
- mutationsPtr
- önkényes információ, ami a gyűjtemény megváltozásának megállapítására lehet használni.
- extra
- objektumok C tömbje, amit a visszatérési értékek megtartására lehet használni.
Példa - láncolt lista
A példában egy C típusú láncolt listát fogunk használni a műveletek megvalósításának bemutatása nélkül
(ezek nyilván senkinek nem okoznak sok gondot). A iteráció során egyesével adjuk vissza az elemeket.
MyChainedListNode.h
#import <Foundation/Foundation.h>
@interface MyChainedListNode : NSObject
{
-(id)value;
-(MyChainedListNode*)next;
}
- (MyChainedListNode*) initWithValue: (id)val;
- (id) getValue;
- (void) setNext: (MyChainedListNode*)node;
- (void) getNext;
@end
MyChainedList.h
#import "MyChainedListNode.h"
@interface MyChainedList : NSObject<NSFastEnumeration>
{
-(MyChainedListNode*)start;
-(MyChainedListNode*)end;
-(NSInteger)size;
-(long)mutated; //ha valtozik a lista mutated=1
}
- (MyChainedList*) init;
- (NSInteger) count;
- (void) add: (id) value;
//...
@end
MyChainedList.m
#import "MyChainedList.h
@implementation MyChainedList
//...
-(NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len
{
state->itemsPtr = stackbuf;
if(state->state == 0) //inicializalas
{
state->mutationsPtr = &mutated;
state->state=1;
mutated = 0;
if (count>0)
{
extra[0] = (long)start;
stackbuf[0] = [start getValue];
return 1;
}
else
{
return 0;
}
}
if (mutated)
{
//dobj kivetelt, ha nem szeretned, hogy modositani lehessen a listat iteracio kozben
}
MyChainedListNode* node = (MyChainedListNode*)extra[0];
node = [node getNext];
if (node)
{
extra[0] = (long)node;
stackbuf[0] = [start getValue];
return 1;
}
else
{
return 0;
}
}
//...
@end
Link
További információ megtalálható a
http://developer.apple.com/library/ios/#documentation/cocoa/Reference/NSFastEnumeration_protocol/Reference/NSFastEnumeration.html oldalon
angol nyelven, illetve példa található a
http://developer.apple.com/library/ios/#samplecode/FastEnumerationSample/Introduction/Intro.html oldalon.