アプリをARCに変換したところ、ビューコントローラーの1つに割り当てられたオブジェクトが、そのビューコントローラーが割り当て解除されたときに割り当て解除されていないことに気付きました。その理由を理解するのに少し時間がかかりました。デバッグ中にプロジェクトで[ゾンビオブジェクトを有効にする]をオンにしましたが、これが原因であることが判明しました。次のアプリロジックを検討してください。
1)ユーザーがRootViewController
でアクションを呼び出し、SecondaryViewController
が作成されてpresentModalViewController:animated
を介して表示されます。
2)SecondaryViewController
には、ActionsController
サブクラスであるNSObject
が含まれています。
3)ActionsController
は、初期化されるとNSNotificationCenter
を介して通知を監視し、割り当てが解除されると監視を停止します。
4)ユーザーはSecondaryViewController
を閉じてRootViewController
に戻ります。
[ゾンビオブジェクトを有効にする]をオフにすると、上記は正常に機能し、すべてのオブジェクトの割り当てが解除されます。ゾンビオブジェクトを有効にすると、ActionsController
が割り当て解除されても、SecondaryViewController
は割り当て解除されません。
これにより、アプリで問題が発生しました。b/ c NSNotificationCenter
は引き続きActionsController
に通知を送信し、結果として生じるハンドラーによってアプリがクラッシュします。
https://github.com/xjones/XJARCTestApp でこれを説明する簡単なアプリを作成しました。これを確認するには、[ゾンビオブジェクトを有効にする]のオン/オフを指定してコンソールログを確認します。
質問(S)
編集#1:ケビンの提案に従って私はこれをApple and openradar at http://openradar.appspot.com/10537635 。
編集#2:良い答えの説明
まず、私は経験豊富なiOS開発者であり、ARC、ゾンビオブジェクトなどを完全に理解しています。もちろん、何かが足りない場合は、照明に感謝します。
次に、この特定のクラッシュの回避策は、actionsController
の割り当てが解除されたときにオブザーバーとしてsecondaryViewController
を削除することです。また、secondaryViewController
がdeallocされたときにactionsController = nil
を明示的に設定すると、deallocされることもわかりました。これらはどちらも優れた回避策ではありませんが、効果的にARCを使用する必要がありますが、ARCを使用していないかのようにコーディングします(たとえば、deallocで明示的にnil iVars)。特定の解決策は、これが他のコントローラーでいつ問題になるかを特定するのにも役立ちません。そのため、開発者はこの問題をいつ/どのように回避するかを決定論的に知っています。
良い答えは、ARC + NSZombieEnabledを使用するときにオブジェクトに対して何か特別なことをする必要があることを決定論的に知る方法を説明するので、この特定の例を解決し、他の同様の可能性を残さずにプロジェクト全体に一般的に適用されます問題。
これはXCodeのバグである可能性があるため、適切な回答が存在しない可能性は十分にあります。
皆さんありがとう!
IOSのバグであることが判明しました。 Appleから連絡があり、iOS6でこれが修正されたことが示されました。
ゾンビが最初に書いたように機能した場合、ゾンビをオンにすると、無数の誤検知が直接発生します...
おそらく_objc_rootRelease
で、いくつかのisa-swizzlingが発生しているため、ゾンビを有効にしてdealloc
のオーバーライドを呼び出す必要があります。ゾンビで発生しない唯一のことは、object_dispose
への実際の呼び出しです—少なくともデフォルトではありません。
面白いのは、少しロギングを行うと、ARCが有効になっている場合でも、dealloc
の実装がそのスーパークラスの実装を呼び出すことが実際にわかることです。
私は実際にはこれをまったく見ないと思っていました:ARCはクラスの.cxx_destruct
ivarを破棄するためにこれらのファンキーな__strong
メソッドを生成するので、私はを見ることを期待していましたthisメソッド呼び出しdealloc
—実装されている場合。
どうやら、NSZombieEnabled
をYES
に設定すると、.cxx_destruct
がまったく呼び出されなくなります—少なくとも、サンプルプロジェクトを編集したときに起こったことです。
[。
興味がある場合は、追加のロギングが含まれています サンプルプロジェクトのフォーク —実行するだけで機能します。ゾンビのオン/オフには2つの共有スキームがあります。
これはバグではなく、機能です。
そしてそれはARCとは何の関係もありません。
NSZombieEnabled
は基本的に実装のdealloc
をスウィズルし、そのオブジェクトのタイプを_NSZombie
—メッセージを送信するとすぐに爆発するダミークラスにisa-スウィズルします。それ。これは予想される動作であり、完全に誤解されていない場合は文書化されています。
これは、Apple in Technical Q&A QA1758 で確認されているバグです。
このコードをアプリにコンパイルすることで、iOS5とOSX10.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を閉じて、再試行できます。また、コードにサークル保持条件があるかどうかを再確認してください。