Objective-C Blocks を使ってみる 1

iMacを買って、せっかく新しい開発環境も揃ったので、早速気になるブロック構文を試してみます。一般に、クロージャとか呼ばれているものに似ているらしいですが、そもそもクロージャ自体あまり知らないので、恐る恐る試している次第です。とりあえず手始めに、Wikipediaに書いてあるクロージャコードを、Objective-Cで書いてみることにしました。

クロージャ(Wikipedia)のJavascriptの例

function newCounter() {
     var i = 0;
     return function() { // 無名関数
         i = i + 1;
         return i;
     }
 }

 c1 = newCounter();
 alert(c1()); // 1
 alert(c1()); // 2
 alert(c1()); // 3
 alert(c1()); // 4
 alert(c1()); // 5

Objective-Cで書くと、こうなるはずです。

#import <Foundation/Foundation.h>

typedef int (^Counter)(void);

Counter newCounter()
{
	__block int i = 0;
	return [[^(void) {
		i += 1;
		return i;
	} copy] autorelease];
}

int main (int argc, const char * argv[]) {
    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];

	Counter c1 = newCounter();
	NSLog( @"%d", c1() ); //1
	NSLog( @"%d", c1() ); //2
	NSLog( @"%d", c1() ); //3
	NSLog( @"%d", c1() ); //4
	NSLog( @"%d", c1() ); //5

    [pool drain];
    return 0;
}

あまり綺麗でないですね。綺麗にならない理由をちょっと解説。

もっとも大きな理由は、ブロックはローカルスコープを抜け出すと、ブロックが消失してしまう点です。なので、コピーメソッドを呼び出して、コピーしておきます。コピーするということは、どこかで破棄しなければならないので、autoreleaseメソッドも投げるわけです。ちなみに、見ての通りブロックにはメッセージ式でメッセージを投げることができるようです。Cでやる場合には、Block_copy()やBlock_release()関数を使用します。

もうひとつ、変数iの宣言に修飾子が付いています。これは、ブロックの中で変数iの値を変更できるようにするためのものです。デフォルトでは、ブロックの外側の変数は、読み出しのみ可能で、書き込みはできないようです。なので、__block修飾子を付けて変数を宣言します。

最後に、newCounterの戻り値の型です。どうしても、戻り値の型に直接ブロックの型を記述する事ができなかったので、上でtypedefして一旦型定義をし、その後で関数定義しています。どういう書き方をすれば直接書けるのか、よくわかりません。

とりあえず、なんとなくブロックというものがわかりました。しかし、まだ使い道が思いつかないので、ドキュメントを見ながらいろいろ試してみることにします。

コメント

  1. Saturn より:

    はじめまして、ちょくちょく参考にさせてもらってます、ありがとう。
    で、戻り値の型に直接ブロックの型を記述するには次のようにすればいいと思います。

    int (^newCounter())()

  2. OverTaker より:

    返信ありがとうございます。たしかに、直接戻り値に型を書くことができました。
    しかし、今度はnewCounter()の戻り値を受け取るところの宣言がどうもうまくいかないので、試行錯誤中です。なかなか慣れない物ですね。

コメント投稿