ブロック構文の基本
blockについて「ブロックプログラミングトピック」を読んで少し理解する。
基本概要
ブロックオブジェクトは、その場限りの関数の本体を、CやCから派生した言語の式として作成する手段を提供します。
コールバックとして特に便利です。それは、ブロックが、コールバックで実行するコードと、その実行中に必要なデータの両方を保持するからです。
ブロック変数を宣言し、ブロックリテラルの始まりを表すには、^演算子を使用します。ブロックの本体は、{}で囲まれます。
int multiplier = 7; int (^myBlock)(int) = ^(int num) { return num * multiplier; }; printf("%d", myBlock(3));// "21"と出力する
クロージャの内部から,スコープの外の変数にアクセスすることができます
int noHoge = 0; __block int blockHoge = 0; ^{ noHoge++; //=> error: increment of read-only variable ‘ro’ blockHoge++; //=> 1 }();
多くの場合、ブロック変数を宣言する必要はありません。その代わりに、引数として必要になる場所にブロックリテラルをインラインで記述します。
ブロックの強力な機能の1つは、同じレキシカルスコープ内の変数を変更できることです。
ブロックの機能
ブロックは、以下のような特徴を持つ無名のインラインコードの集合体です。
- 関数と同様に、型付きの引数リストを持つ
- 推定または宣言された戻り値型を持つ
- ブロックの定義を含むレキシカルスコープの状態を把握できる
- 必要であれば、レキシカルスコープの状態を変更できる
- 変更できるかどうかは、同じレキシカルスコープ内で定義されているほかのブロックと共通である
- レキシカルスコープ(スタックフレーム)が破棄された後も、引き続きそのレキシカルスコープ内で定義されている状態を共有したり変更したりできる
通常、ブロックは自己完結的な小さなコード部品です。このため、ブロックは、並列に実行されたり、1つのコレクション内の複数のアイテムを対象に実行される一連の作業をカプセル化する手段として、また、別の操作が終了したときのコールバックとして、特に有用です。
- ブロックを利用すると、メソッド実装のコンテキストで後から実行されるコードを、呼び出しの時点で記述できます。したがって、通常、ブロックはフレームワークメソッドのパラメータになります。
- ブロックでは、ローカル変数にアクセスできます。操作の実行に必要なすべてのコンテキスト情報を具体化したデータ構造を要求するコールバックを使用する代わりに、単純にローカル変数に直接アクセスできます。
ブロックは次の2つのタイプの変数もサポートします。
- 関数レベルの__block変数。これらの変数は、ブロック(およびスコープ)内では可変です。また、参照しているブロックがヒープにコピーされている場合は保持されます
- constのインポート
メソッドの実装内でブロックを使用する場合は、オブジェクトのインスタンス変数のメモリ管理規則はさらに厳しくなります。
dispatch_async(queue, ^{ // instanceVariableは参照渡しで使われるため、selfは保持される doSomethingWithObject(instanceVariable); }); id localVariable = instanceVariable; dispatch_async(queue, ^{ // localVariableは値渡しで使うため、localVariableは保持される(selfではない) doSomethingWithObject(localVariable); });
コピー
ブロックをコピーするとき、そのブロック内からほかのブロックへの参照があれば、それらは必要に応じてコピーされます(トップからツリー全体がコピーされる場合もあります)。ブロック変数が存在し、ブロック内からそのブロックを参照すると、そのブロックがコピーされます。
参考
https://developer.apple.com/jp/devcenter/ios/library/documentation/Blocks.pdf