はじめに
ARC、Blocks、llvm4.0 Modern Objective-Cと、Objective-Cの記述方法はどんどんん変わっています。コードの記述量が減ったり、分かりやすいコードを書くことが出来る様になって来たものの、リーク、EXC_BAD_ACCESS(解放されたメモリにアクセスすることによるクラッシュ)が発生する危険があります。この記事では、2012年8月のリリースされた機能の時点で、わたしが採用している書き方のルールを紹介します。
iOS5 + weak最強
前提条件として、__weakが使用出来るiOS5での状況について記述しています。UI要素の変更の為にBlocksをコールバックで使う状況で、iOS4+ARC+Blocksで完全に問題が起きないコードを書くのは結構大変です。
決まりを作る
それで実際にプログラムを作成するのですが、その時その時でベストな方法を選べば良いのですが、それだと、作っている人のスキルに依存するため、ルールを決めてしまって、そのルールを守る様にしています。特にこれはスキルや、言語理解の異なるチームで行う際には重要ですね。
ARC+Blocks+Xcode4.4での4つの決まり
いかの4つの決まりを作成して運用を開始しています。
- ivarを作成せず、すべて@propertyを作成する
- Blocks内では、ivar、selfには決してアクセスしない
- Blocks内でアクセスする用に__weakで、wselfポインタを作成する
- IBOutletは、@propety (weak) で接続する
少し前までは、@interfaceに、ivarを作成するのが普通でしたが、最近は、@implementationに作成する方が、ヘッダファイルに不要な変数を入れないので普通になったと思います。Xcode4.4のllvm4.0から、synthesized by defaultというのが採用され、@synthesizeを書かなくても、
このように記述することによって、
上の@synthesizeが自動的にコンパイラによって補完されます。詳しくは、今日からライオンでも使える!Xcode4.4 Modern Objective-C Syntaxでコードをきれいにする方法 | Zero4Racer PRO Developer’s Blog の記事をご覧下さい。
基本的にはすべて.mファイルに、無名カテゴリを使用して作成しますが、別のファイルからアクセスするプロパティは、ヘッダファイルに移動します。
変数にアクセスする際は、self.mycount, _mycount 両方使用することが出来ますが、init系のメソッド内、また、dealloc内では、必ず_で始まるivarを使用します。
これは、こうしなくてもうまくいく場合は確かにあります。blocksが、将来の実行のためにコピーされた時に、循環参照が起きる危険が発生します。通常、ブロックを作成したスレッドと同じスレッドで、連続してBlockを実行する状況では、Blockはコピーされないため、selfを使っていても循環参照しません。
[IOS5]FOR (;;) を書くのをやめるいくつかの方法と、BLOCKS+ARCの考察 | Zero4Racer PRO Developer’s Blog この記事で少し説明しました。しかし、blocksを渡した先でどのように処理するかは分かりませんし、パフォーマンスのために、後から動作の方法を変えることもあります。ですから,blocksの中で self.と書くのは禁止します。そして_mycountなどの、ivarにアクセスすると、self->_mycountとみなされて、知らずにselfがretainされてしまい、循環参照が起きることがあります。なので、blocksの中でのivarの使用も禁止です。
サンプルで、だめな記述の例(チェックで発見したもの)を載せときます。
(2)で禁止したselfへのアクセスの代わりに、__weak型のselfポインタを作成します。
こんな感じです。クラスは、現在のクラスの型ですね。そして、blocksの中では、ivarにアクセスせず、wselfに対するプロパティでアクセスします。修正後はこんな感じです。
Blocks内すべてを書き換える必要があるので、検索で”^{“の物をすべて検索して、一つづつ修正していきます。
注意すべきなのが、以前は、wself->_mycountのように、->を使って、ポインタのivarにアクセスすることが出来ていましたが、llvm4.0から、__weak ポインタに対しては、-> でのivarのアクセスが出来なくなりました。
ですから、(1)のようにすべてpropertyにする必要がある訳ですね。
こちらはおまけですが、IBOutletで、Storyboardと連結するときですが、既にそれぞれのコンポーネントは、viewに追加されているため、weakで接続するのが好ましいです。なので、IBOutletは、以下の様に記述します。
まとめ
参考にしたサイトは、Appleのドキュメントと、kishikawa katsumi (k_katsumi) on Twitter さんの、iPhone_dev_jpでのスライドです。
突っ込みどころもたくさんあると思いますので、何かあったらTomohisa Takaoka (tomohisa) on Twitter か、コメントで。
「ARC+Blocks+llvm4.0時代のコード記述作法」への1件のフィードバック