【Objective-C】なぜブロックを copy して保持するのか

ブロックをプロパティに保持したいとき、下記のようにブロックをコピーしますが、参照カウントを増やすのではなくコピーして保持するのはなぜでしょうか。

@propaty(nonatomic, copy) void (^hoge)();

self.hoge = ^{...};

 

答えは、ブロックがスタック領域に作成されるからです。

スタック領域とは、ローカル変数など特定のスコープの中でのみ使用されるデータを記録しておく場所です。スコープから出る (例えば、メソッドで return を呼び出して終了するなど) と自動的に破棄されます。つまり、参照カウントを増やそうとしても解放されてしまうのです。

スコープから出てもメモリ上に残しておくためには、ヒープ領域を使う必要があります。

ヒープ領域とは、メモリを動的に確保したり解放したりできる場所です。実行してからしか必要なメモリのサイズがわからない場合、ここが使われます。ここでは解放する処理(参照カウントを減らすなど)をするまで残ったままになります。
例えば、
poge = [Poge new];
などとして新規にオブジェクトを作成したときに使われます。
なので、copy (オブジェクトのコピーを新たに作成) すると、ヒープ領域に作成され、スコープを出ても保持されたままになるのです。

参考

【追記】

  • copy じゃなくて strong にしても動く謎…。
  • Objective-C のオブジェクトはヒープ領域に作成されます。ただし、NSString *str = @"aaa"; としたとき、文字列オブジェクトはヒープ領域に作成されますが、文字列オブジェクトをさすアドレスはスタック領域に保存されるようです。