Sunflat のブログ

ソフトウェア開発についての話題が多いかも

Objective-C (Cocoa)でIteratorパターンを実装してみた

Objective-C (Cocoa)でIteratorパターンを実装してみたので、貼っておきます。
NSEnumerator(外部Iterator)、高速列挙、Blocks の3種類の方法を実装してあります。

参考:

BookShelf.h

#import <Foundation/Foundation.h>

@interface Book : NSObject {
@private
    NSString *mName;
}

@property (readonly) NSString *name;
- (id)initWithName:(NSString *)name;
+ (Book *)bookWithName:(NSString *)name;

@end

/////

@interface BookShelf : NSObject<NSFastEnumeration> {
@private
    NSMutableArray *mBooks;
}

@property (readonly) int length;
- (Book *)bookAt:(int) index;
- (void)appendBook:(Book *)book;
- (void)foreach:(void (^)(Book *))block;
- (NSEnumerator *)bookEnumerator;

@end

BookShelf.m

#import "BookShelf.h"

@implementation Book

@synthesize name = mName;

- (id)initWithName:(NSString *)name {
    self = [super init];
    if (self) {
        mName = [name retain];
    }
    return self;
}

+ (Book *)bookWithName:(NSString *)name {
    return [[[Book alloc] initWithName:name] autorelease];
}

- (void)dealloc {
    [mName release];
    [super dealloc];
}

@end

/////

@interface BookShelfEnumerator : NSEnumerator {
@private
    BookShelf *mShelf;
    int mIndex;
}
@end

@implementation BookShelfEnumerator

- (id)initWithBookShelf:(BookShelf *)shelf {
    self = [super init];
    if (self) {
        mShelf = shelf;
    }
    return self;
}

- (id)nextObject {
    id obj=(mIndex>=mShelf.length)? nil : [mShelf bookAt:mIndex];
    mIndex++;
    return obj;
}

@end

/////

@implementation BookShelf

- (id)init {
    self = [super init];
    if (self) {
        mBooks = [[NSMutableArray alloc] init];
    }
    return self;
}

- (void)dealloc {
    [mBooks release];
    [super dealloc];
}

- (int)length {
    return [mBooks count];
}

- (Book *)bookAt:(int) index {
    return [mBooks objectAtIndex:index];
}

- (void)appendBook:(Book *)book {
    [mBooks addObject:book];
}

// NSEnumerator(外部Iterator)を使った列挙の実装
- (NSEnumerator *)bookEnumerator {
    return [[[BookShelfEnumerator alloc] initWithBookShelf:self] autorelease];
}

// 高速列挙の実装
- (NSUInteger)countByEnumeratingWithState:(NSFastEnumerationState *)state objects:(id *)stackbuf count:(NSUInteger)len {
    int count = 0;
    while(state->state < self.length && count < len) {
        stackbuf[count++] = [self bookAt:state->state++];
    }
    state->itemsPtr = stackbuf;
    state->mutationsPtr = (unsigned long*)self;
    return count; 
}

// Blocksを使った列挙の実装
- (void)foreach:(void (^)(Book *))block {
    for(Book *book in mBooks) {
        block(book);
    }
}

@end

利用例

#import "BookShelf.h"

- (void)testBookShelf {
    BookShelf *shelf=[[BookShelf alloc] init];
    [shelf appendBook: [Book bookWithName: @"Around the World in 80 Days"] ];
    [shelf appendBook: [Book bookWithName: @"Bible"] ];
    [shelf appendBook: [Book bookWithName: @"Cinderella"] ];
    [shelf appendBook: [Book bookWithName: @"Daddy-Long-Legs"] ];
    
    // NSEnumeratorの利用
    {
        int count=0;
        NSEnumerator *e=[shelf bookEnumerator];
        Book *book;
        while((book = [e nextObject])) {
            NSLog(@"%@", book.name);
            ++count;
        }
        NSLog(@"Total %d books.", count);
    }
    
    // 高速列挙の利用
    {
        int count=0;
        for(Book *book in shelf) {
            NSLog(@"%@", book.name);
            ++count;
        }
        NSLog(@"Total %d books.", count);
    }
    
    // Blocksを使った列挙の利用
    {
        __block int count=0;
        [shelf foreach:^(Book *book) {
            NSLog(@"%@", book.name);
            ++count;
        }];
        NSLog(@"Total %d books.", count);
    }
    
    //
    [shelf release];
}