Objective-Cのブロック構文をメソッドの引数にする

2014年03月25日追記
http://fuckingblocksyntax.com/という、ちょっと発声できない名前のページに、分かりやすくまとまっているのでそちらを参照するとよいかもしれないです。

概要

Objective-Cでは、ブロック構文というものが使える。iOS4から使えるようになった。
例えば、配列を走査したい場合はこんな感じで書ける。

NSArray *array = [NSArray arrayWithObjects:
				  @"りんご", @"ごま",
				  @"まほうしょうじょまどかまぎか",
				  @"からす", @"すずめ",
				  @"めだか", @"かんり", nil];
[array enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
	NSLog(@"%d番目の要素は「%@」です。",idx,obj);
}];

これを自分で書いたクラスの中でメソッドの引数にするにはどうするか、というお話。

そのまま直に書く

2013年11月28日追記
NSArray#enumerateObjectsUsingBlockにJump to Definitionしたらどう実装してたか普通に分かった。引数の型にvoid (^)(引数...)という書式で書けばオーケー。以下は例。

@interface SampleClass
- (void)countRandomUsingBlock:(void (^)(NSUInteger index, NSString *value))block length:(NSUInteger)length;
@end

@implementation SampleClass
- (void)countRandomUsingBlock:(void (^)(NSUInteger index, NSString *value))block length:(NSUInteger)length {
	srand(time(nil));
	for (int i = 0; i < length; i++) {
		NSString *value = [NSString stringWithFormat:@"%d",rand()];
		block(i, value);
	}
}
@end

型を新たに定義する

詳しいことはApple公式のpdf「ブロックプログラミングトピック」を確認したほうがよい。
typedefを使ってブロックの型を宣言して、それをメソッドの引数の型として指定してやればよい。以下は例。

typedef void (^MYOperatorEnumerationResultsBlock)(NSUInteger index, NSString *value);

@interface MYOperator : NSObject
- (void)countRandomUsingBlock:(MYOperatorEnumerationResultsBlock)enumerationBlock length:(NSUInteger)length;
@end

@implementation MYOperator
- (void)countRandomUsingBlock:(MYOperatorEnumerationResultsBlock)enumerationBlock length:(NSUInteger)length {
	srand(time(nil));
	for (int i = 0; i < length; i++) {
		NSString *value = [NSString stringWithFormat:@"%d",rand()];
		enumerationBlock(i, value);
	}
}
@end

メソッドの呼び出し

実際に呼び出したいところでは、こんな感じで書く。
呼び出す時はXcodeの入力補完がとても便利。

    MYOperator *operator = [[[MYOperator alloc] init] autorelease];
    [operator countRandomUsingBlock:^(NSUInteger index, NSString *value) {
        NSLog(@"%d is %@", index, value);
    } length:5];

コメントを残す