ARC + NSZombieEnabled を使用するとオブジェクトのロックが解除されないのはなぜですか
-
29-10-2019 - |
質問
アプリを ARC に変換したところ、ビュー コントローラーの 1 つに割り当てられたオブジェクトが、そのビュー コントローラーの割り当てが解除されたときに割り当てが解除されていないことに気付きました。理由を理解するのに時間がかかりました。デバッグ中にプロジェクトで [ゾンビ オブジェクトを有効にする] をオンにしているのですが、これが原因であることが判明しました。次のアプリのロジックを考えてみましょう。
1) ユーザーがアクションを呼び出す RootViewController
それは SecondaryViewController
を通じて作成および提示されます presentModalViewController:animated
.
2) SecondaryViewController
が含まれています ActionsController
それは NSObject
サブクラス。
3) ActionsController
経由で通知を監視します NSNotificationCenter
初期化されるとき、割り当てが解除されると監視を停止します。
4) ユーザーが拒否する SecondaryViewController
に戻る RootViewController
.
[ゾンビ オブジェクトを有効にする] をオフにすると、上記は正常に動作し、すべてのオブジェクトの割り当てが解除されます。ゾンビオブジェクトを有効にするをオンにした場合 ActionsController
にもかかわらず割り当てが解除されていない SecondaryViewController
割り当てが解除されます。
これにより、アプリで問題が発生しました NSNotificationCenter
に通知を送信し続けます ActionsController
結果として生じるハンドラーによりアプリがクラッシュします。
これを説明する簡単なアプリを作成しました。 https://github.com/xjones/XJARCTestApp. 。これを確認するには、[ゾンビ オブジェクトを有効にする] をオン/オフにしてコンソール ログを調べます。
質問
- これは、ゾンビ オブジェクトを有効にするの正しい動作ですか?
- 問題を解決するには、この種のロジックをどのように実装すればよいでしょうか。引き続き「Enable Zombie Objects」を使用したいと思います。
編集#1:Kevin の提案に従い、これを Apple と openradar に提出しました。 http://openradar.appspot.com/10537635.
編集#2:良い答えについての説明
まず、私は経験豊富な iOS 開発者であり、ARC、ゾンビ オブジェクトなどを完全に理解しています。もちろん、何かが足りない場合は、照明があるとありがたいです。
第 2 に、この特定のクラッシュの回避策は次のとおりです。 actionsController
そのときの観察者として secondaryViewController
割り当てが解除されます。また、明示的に設定すると、 actionsController = nil
いつ secondaryViewController
割り当てが解除されると、割り当てが解除されます。これらは両方とも、事実上 ARC を使用する必要があるため、優れた回避策ではありませんが、ARC を使用していないかのようにコーディングする必要があります (例:dealloc で明示的に nil iVars)。また、特定の解決策は、これが他のコントローラーで問題となる時期を特定するのにも役立ちません。そのため、開発者は、この問題をいつ、どのように回避するかを決定的に知ることができます。
良い答えは、ARC + NSZombieEnabled を使用するときにオブジェクトに関して何か特別なことをする必要があることを決定論的に知る方法を説明し、この特定の例を解決し、他の同様の可能性を残さずにプロジェクト全体に一般的に適用できるようにすることです。問題。
これは XCode のバグである可能性があるため、適切な答えが存在しない可能性は十分にあります。
皆さんありがとう!
解決 3
iOSのバグであることが分かりました。Apple から私に連絡があり、iOS 6 でこの問題を修正したとのことです。
他のヒント
結局のところ、私はいくつかの重大なナンセンスを書いていました
最初に書いたようにゾンビが機能する場合、ゾンビをオンにすると無数の誤検知が直接発生するでしょう...
おそらく、いくつかの isa-swizzling が起こっています。 _objc_rootRelease
, 、したがって、 dealloc
ゾンビを有効にして呼び出す必要があります。ゾンビで起こらない唯一のことは、実際の呼び出しです。 object_dispose
— 少なくともデフォルトではそうではありません。
面白いのは、少しロギングを実行すると、ARC が有効になっているにもかかわらず、 dealloc
スーパークラスの実装を呼び出します。
私は実際にはこれがまったく表示されないと想定していました:ARC はこれらのファンキーなサウンドを生成するため、 .cxx_destruct
何かを処分する方法 __strong
クラスのイーヴァル、私は見ることを期待していました これ メソッド呼び出し dealloc
— 実装されていれば。
どうやら設定では、 NSZombieEnabled
に YES
原因 .cxx_destruct
まったく呼び出されないようにします。少なくとも、私がサンプル プロジェクトを編集したときには、次のようなことが起こりました。
ゾンビをオフにするとバックトレースと両方の解放が行われますが、ゾンビがオンではバックトレースは行われず、解放は 1 つだけ行われます。
興味がある場合は、追加のログが以下に含まれています。 サンプルプロジェクトのフォーク — 実行するだけで動作します:ゾンビのオン/オフには 2 つの共有スキームがあります。
元の(無意味な)答え:
これはバグではなく機能です。
ARCとは全く関係ありません。
NSZombieEnabled
基本的にスウィズル dealloc
実装では、そのオブジェクトの型を isa-swizzle に変更します。 _NSZombie
— メッセージを送信するとすぐに爆発するダミー クラス。これは予期された動作であり、私が完全に間違っていなければ、文書化されています。
これは Apple によって認められているバグです。 技術Q&A QA1758.
iOS 5 および OS X 10.7 では、このコードをアプリにコンパイルすることで回避できます。
#import <objc/runtime.h>
@implementation NSObject (ARCZombie)
+ (void) load
{
const char *NSZombieEnabled = getenv("NSZombieEnabled");
if (NSZombieEnabled && tolower(NSZombieEnabled[0]) == 'y')
{
Method dealloc = class_getInstanceMethod(self, @selector(dealloc));
Method arczombie_dealloc = class_getInstanceMethod(self, @selector(arczombie_dealloc));
method_exchangeImplementations(dealloc, arczombie_dealloc);
}
}
- (void) arczombie_dealloc
{
Class aliveClass = object_getClass(self);
[self arczombie_dealloc];
Class zombieClass = object_getClass(self);
object_setClass(self, aliveClass);
objc_destructInstance(self);
object_setClass(self, zombieClass);
}
@end
この回避策の詳細については、私のブログ投稿を参照してください。 ARC とゾンビを有効にしてデバッグする.
2 番目の質問に答えるには、NSNotification からオブザーバーを削除する必要があります。これにより、ビューが呼び出されなくなります。
通常、これを dealloc で実行しますが、ゾンビの問題により呼び出されない可能性があります。おそらくそのロジックを viewDidUnload に入れることができますか?
NSZombieEnabled が開いているので、オブジェクトは dealloc を呼び出さず、オブジェクトを特別な場所に置きます。NSZombieEnabled を閉じて、もう一度試してください。そして、コードにサークル保持条件があるかどうかを再確認してください。