クラスの継承で従来コードと ARC コードが混在する場合 - Automatic Reference Counting
SPECIAL
クラスの継承で従来コードと ARC コードが混在する場合
ARC コードと従来のコードの混在について で記したように、ひとつのプロジェクトの中で ARC 対応のコードと非対応のコードとをファイル毎に混在させることができます。
通常はこれで問題なく相互のやり取りが自然にできるのですが、基底クラスと派生クラスとで ARC の対応状況が違う場合に注意する必要が出てくる場合があります。
派生元が従来コードの場合はオーナーシップに注意
それは、従来のコードで作成したクラスのインスタンス変数のアクセスレベルを @protected や @public にしていて、それを ARC コードの派生先のクラス(または外部の ARC ソースコード)から直接アクセスする場合です。
外部からインスタンス変数をアクセスできるとすると、そのインスタンス変数はヘッダーファイルで定義されていると思います。
従来のコードであれば、そのインスタンス変数の宣言で __strong や __unsafe_unretained といったオーナーシップはまず宣言されていないと思いますが、それを ARC コードで利用すると、既定のオーナーシップである __strong として扱われてしまう様子です。
従来のコードは、従来のコードの中であれば __unsafe_unretained に近い動作をするため、この違いがプログラミングに影響を与える場合があります。
もちろん、派生前の従来のコードの動作がおかしくなることはないので、継承先のクラスで注意すれば大丈夫です。
ただし、いわゆる __unsafe_unretaind なインスタンス変数を扱うときは注意が必要です。
従来のコードの継承元としては retain せずに保持させておきたいインスタンス変数であっても、継承先の ARC コードでそこに代入すると retain されてしまうので、インスタンス変数を直接操作しているとできないことも出てくるかもしれません。
派生先が従来コードの場合は weak の扱いに注意
逆に ARC コードの基底クラスを従来のコードで継承する場合は、従来通り、retain や release を意識したプログラミングになるだけなので、これらについてはプログラムを組む上で、それほど思いがけないことはなさそうです。
ところで、ARC コードのクラスを継承するということは、従来のコードの継承先で __strong や __weak といったオーナーシップが指定された変数定義をインポートすることになります。
これらはたしか ARC が登場した最初の頃は従来コードに登場するとエラーになっていたような気がするのですけど、とりあえず今 Xcode 4.5.2 で試してみている限りではエラーにならないようなので、そんなコンパイラ周りの心配も要らない様子です。
ただ、今回のケースで気を遣わないといけないのが weak 周りの挙動です。
従来コードの場合、変数に値を代入するだけでは何も起こらないのですが、この何も起こらないことによって weak の挙動が影響を受けます。
ARC コードで __weak 宣言して使用しているインスタンス変数を、従来コードで直接操作する場合に起こる問題点を整理すると、次のような感じでした。
- 従来のコードで __weak なインスタンス変数に新しい値を代入すると、その変数を ARC 環境で参照するまでの間は使える。ARC 環境で参照した時点で、そのインスタンス変数の値は nil になる。
- 従来コードから __weak なインスタンス変数への代入であっても、代入済みの値と同じ値の代入であれば問題ない。
- 従来コードで __weak なインスタンス変数の元のインスタンスを retain すると、リリースするまで解放されなくなる。
- 従来コードで __weak なインスタンス変数の元のインスタンスを release しても正常に機能する。これによって元のインスタンスが破棄された場合、__weak なインスタンス変数を参照した時に nil が得られる。
もしかすると __weak 変数というのは設定時に状態を覚えておいて、取得時に矛盾していたり、無効になっていた場合に nil を返す仕組みなのかもしれません。
従来コードから __weak 変数に触れると代入時の配慮がされないため、ARC コードも含めた全体で、期待した動作が得られなくなってしまうようでした。
このことから、従来コードの派生クラスから ARC コードの基底クラスの __weak インスタンス変数には触れないと思って良さそうです。
もちろん、ARC コードに実装された weak 指定のプロパティを介して読み書きすることは可能ですけど、従来コードでは __weak 変数で受けられないので、結局は __unsafe_unretained なものとして扱わなければいけなくなると思います。