web-dev-qa-db-ja.com

ARC + NSZombieEnabledを使用しているときに、オブジェクトの割り当てが解除されないのはなぜですか

アプリを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. これは、ゾンビオブジェクトを有効にするの正し​​い動作ですか?
  2. この問題を解決するには、このタイプのロジックをどのように実装すればよいですか。引き続き「ゾンビオブジェクトを有効にする」を使用したいと思います。

編集#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のバグである可能性があるため、適切な回答が存在しない可能性は十分にあります。

皆さんありがとう!

35
XJones

IOSのバグであることが判明しました。 Appleから連絡があり、iOS6でこれが修正されたことが示されました。

4
XJones

結局、私はいくつかの深刻なナンセンスを書いた

ゾンビが最初に書いたように機能した場合、ゾンビをオンにすると、無数の誤検知が直接発生します...

おそらく_objc_rootReleaseで、いくつかのisa-swizzlingが発生しているため、ゾンビを有効にしてdeallocのオーバーライドを呼び出す必要があります。ゾンビで発生しない唯一のことは、object_disposeへの実際の呼び出しです—少なくともデフォルトではありません。

面白いのは、少しロギングを行うと、ARCが有効になっている場合でも、deallocの実装がそのスーパークラスの実装を呼び出すことが実際にわかることです。

私は実際にはこれをまったく見ないと思っていました:ARCはクラスの.cxx_destruct ivarを破棄するためにこれらのファンキーな__strongメソッドを生成するので、私はを見ることを期待していましたthisメソッド呼び出しdealloc —実装されている場合。

どうやら、NSZombieEnabledYESに設定すると、.cxx_destructがまったく呼び出されなくなります—少なくとも、サンプルプロジェクトを編集したときに起こったことです。
[。

興味がある場合は、追加のロギングが含まれています サンプルプロジェクトのフォーク —実行するだけで機能します。ゾンビのオン/オフには2つの共有スキームがあります。


元の(無意味な)答え:

これはバグではなく、機能です。

そしてそれはARCとは何の関係もありません。

NSZombieEnabledは基本的に実装のdeallocをスウィズルし、そのオブジェクトのタイプを_NSZombie —メッセージを送信するとすぐに爆発するダミークラスにisa-スウィズルします。それ。これは予想される動作であり、完全に誤解されていない場合は文書化されています。

9
danyowdee

これは、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とゾンビを有効にしてデバッグする を参照してください。

7
0xced

2番目の質問に答えるには、NSNotificationからオブザーバーを削除する必要があります。これにより、オブザーバーがビューを呼び出さなくなります。

通常、これはdeallocで行いますが、そのゾンビの問題では、呼び出されない可能性があります。たぶん、そのロジックをviewDidUnloadに入れることができますか?

0
carbonbasednerd

NSZombieEnabledを開いているので、これにより、オブジェクトはdeallocを呼び出さず、オブジェクトを特別な場所に配置します。 NSZombieEnabledを閉じて、再試行できます。また、コードにサークル保持条件があるかどうかを再確認してください。

0
user501836