第3回 @property,@synthesize,retain,assign,copyについて | 渋谷で働くUnity野郎

渋谷で働くUnity野郎

備忘録として有効活用していきます。

さてさて第三回です。
一気に書くと疲れますね。

最後に焦点を当てるのは、ARCが導入されてから新たに扱われるようになった
weakstrongです。



早速ですが、まず、weakとは名前の通りですが、弱参照の事です。
弱参照とは、「持ち主(参照先のオブジェクト)がいなくなる(破棄される)と、自動的にnilが代入されます」
参照先が消えれば、自動的に消える。
要は参照先のオブジェクトと運命を共にするという事ですね。





次に、strongとはお察しとは思いますが、強参照の事です。
強参照とは、一言で言えば、「持ち主になる」という事です。




このweakとstrongの分かりやすい例がIBOutletです。
iphoneアプリ開発で、たびたび(?)お世話にIBOutletですが、これは基本的にweak属性を割り当てます



なぜでしょうか。



IBOutletで宣言する例と言えば、UIButtonやUILabelやUIImageViewなどなどがあります。
これらはコードで書くとaddSubviewしますよね。


つまり親ビュー(トップレベルビュー)が存在して、そこにサブビューとして追加します。



言い換えれば、トップレベルビューがUIButtonやUILabelを所有する(参照)しているので、トップレベルビュー(UIViewやUIWindow)が消えればsubViewは消えても構いませんよね?


ですのでweakが適切という事になります。
(addSubviewするものは全て親ビューが存在するのでweakで構いません。)



勿論ながら、IBOutletでトップレベルビューであるViewやWindowを宣言する場合は、勿論strongで宣言する必要があります。
strongは親クラス、トップレベルビューになってね!と直接言ってるようなものです。


さて、weakとstorongの使い分けについては、この例でなんとなくイメージがついたと思いますが、更に踏み込んでみましょう。



weakの意義ですが、循環参照にあります。



循環参照とは、名前の通り参照が循環してしまってる状況の事です。
これはメモリリークの引き金となります。

循環参照を簡単なコードで記述すると次の通りです。(適切なコードではありません)



#import ClassB
Class A{
//メンバ変数
_strong Obectj b = [ClassB alloc];
}



#import ClassA
Class B{
//メンバ変数
_strong Object a = [ClassA alloc];
}



ClassAはClassBのオブジェクトを持つ。
ClassBはClassAのオブジェクトを持つ。

図にすると次の通りです。

$Iichinのブログ


この場合、ClassAにとったらClassBは親。ClassBにとったらClassAが親になっています。


参照が循環してますねぇ~~


さてさて、これの一体どこが悪いの?という事ですが、
例えばObject bはClassA内で一時的にしか使用しないクラスだった場合の事を考えてみましょう。


当然、ClassA内でObject bを使用する処理が終わった際にメモリ解放しますよね?
しかしながら、この時Object bはrelease出来ません。

なぜならClassB側すると、メンバ変数であるObject aによって、ClassAを強参照しているからです。

分かりやすく例えると、
ClassAはClassBのインスタンス(Object b)を解放します。
ClassA「ClassBのインスタンスさん(Object b)。もういらないよキミ。」

しかし、ClassBのインスタンス側からすると、ClassAのオーナーとなっている(強参照している)ので、解放しようとすると、
Object b「ClassAが存在してるから、俺はまだ死ねないんだああ」
と駄々をこねます。
ClassAは自分自身なのでお手上げ状態です。


逆も然りです。
ClassBのメンバ変数であるClassA型のObject aが存在するため、ClassAを解放させてくれません。

つまり
ClassB「ClassAのインスタンスさん(Object a)。ばいばい。」
Object a「ClassBが存在してるから、まだ死ねないよ!」
こちらも同様に、ClassBは自分自身なのでどうしようもないです。

まだ、第三者のクラス、クラスCがClassAかClassBを解放しようとしても同様にできませんね。

詰まる所、2つのオブジェクトは同時に破棄する事ができないので、メモリ上に永遠に存在し続けるという事になります。

そして、これがメモリリークを引き起こすと…。
イメージが湧きましたか?


これに対する手段がweak属性です。
ClassBから、ClassAの参照を弱参照にしてみます。

コードで書くとこんな感じ。



#import ClassB
Class A{
//メンバ変数
_strong Object b = [ClassB alloc];
}



#import ClassA
Class B{
//メンバ変数
_weak Object a = [ClassA alloc];
}



図で表すとこんな感じ。
$Iichinのブログ

この場合、親がClassBで、子がClassAとなっています。
なのでClassBが「ClassAのインスタンスさん、ばいばい!」と言うのが通用します。

また、第三者のClassCがClassAのObject bを解放すると、Object bが解放されると同時に、ClassBのObject aも同時に解放(nil)になります。

このように、循環参照によるメモリリークを防ぐ仕組みが、weakとstrongなんですね。

delegateなんかはweakで指定します。
親があっての委譲なので…ね。



(3)まとめ
・IBOutletに関してはweakが基本
ただし、トップレベルビュー(UIView,やUIWindow)に関してはstrongが適切

・strongが親オブジェクトを指し、weakが子オブジェクトを指している状態が適切

・weakやstrongは循環参照を防ぐのに役立つ。




少し、この循環参照についての記事は怪しいものがありますので、おかしい点があればご指摘下さい。

参考文献:http://blog.natsuapps.com/2011/11/ios5-arc-strong-reference-cycle.html

第1回 @property,@synthesizeについて

第2回 retain,assign,copyについて