35

アプリを ARC に変換したところ、View Controller の 1 つで割り当てられたオブジェクトが、その View Controller が割り当て解除されたときに割り当て解除されていないことに気付きました。理由を理解するのにしばらく時間がかかりました。デバッグ中にプロジェクトのゾンビ オブジェクトを有効にしましたが、これが原因であることが判明しました。次のアプリ ロジックを検討してください。

1) ユーザーは、を介して を作成および提示するRootViewControllerアクションを呼び出します。SecondaryViewControllerpresentModalViewController:animated

2)サブクラスである がSecondaryViewController含まれています。ActionsControllerNSObject

3)初期化されたときにActionsController通知を監視し、NSNotificationCenter割り当てが解除されたときに監視を停止します。

4) ユーザーは終了SecondaryViewControllerして に戻りRootViewControllerます。

Enable Zombie Objects をオフにすると、上記は正常に機能し、すべてのオブジェクトの割り当てが解除されます。Enable Zombie Objects をオンにすると、割り当てが解除されActionsControllerても、割り当てSecondaryViewControllerが解除されません。

これにより、アプリで問題が発生し、b/cNSNotificationCenterが通知を送信し続けActionsController、結果のハンドラーによってアプリがクラッシュします。

これを示す簡単なアプリをhttps://github.com/xjones/XJARCTestAppで作成しました。これを確認するには、[ゾンビ オブジェクトを有効にする] をオンまたはオフにしてコンソール ログを調べます。

質問

  1. Enable Zombie Objects のこの動作は正しいですか?
  2. 問題を解決するには、このタイプのロジックをどのように実装すればよいですか。Enable Zombie Objects を引き続き使用したいと思います。

編集 #1: Kevin の提案に従って、これを Apple と openradar ( http://openradar.appspot.com/10537635 ) に提出しました。

編集#2:良い答えの明確化

まず、私は経験豊富な iOS 開発者であり、ARC やゾンビ オブジェクトなどを完全に理解しています。

次に、この特定のクラッシュの回避策は、割り当てが解除actionsControllerされたときにオブザーバーとして削除することsecondaryViewControllerです。また、いつ解放されるかを明示的に設定actionsController = nilするsecondaryViewControllerと、解放されることもわかりました。これらはどちらも優れた回避策ではありません。実際には ARC を使用する必要がありますが、ARC を使用していないかのようにコーディングします (dealloc で iVar を明示的に nil にするなど)。特定の解決策は、これが他のコントローラーの問題になる時期を特定するのにも役立ちません。したがって、開発者は、この問題を回避する時期と方法を決定論的に知ることができます。

良い答えは、ARC + NSZombieEnabled を使用するときにオブジェクトに対して何か特別なことをする必要があることを決定論的に知る方法を説明し、この特定の例を解決し、他の同様の可能性を残さずにプロジェクト全体に一般的に適用することです問題。

これは XCode のバグである可能性があるため、適切な答えが存在しない可能性は十分にあります。

皆さんありがとう!

4

5 に答える 5

9

結局のところ、私はいくつかの深刻なナンセンスを書きました

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

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

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

実際には、これはまったく表示されないと想定していました。ARC はこれらのファンキーな.cxx_destructメソッドを生成__strongしてクラスの ivar を破棄するため、このメソッド呼び出しが表示されることを期待していました (dealloc実装されている場合)。

どうやら、に設定NSZombieEnabledするYES.cxx_destruct、まったく呼び出されなくなります — 少なくともサンプル プロジェクトを編集したときはそうでした:
ゾンビをオフにすると、バックトレースが発生し、両方の解放が行われますが、ゾンビをオンにすると、バックトレースが発生せず、解放が 1 つだけになります。

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


元の(無意味な)答え:

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

また、ARCとは何の関係もありません。

NSZombieEnabled基本的deallocには、オブジェクトの型を isa-swizzles する実装を求めてスウィズルします_NSZombie— メッセージを送信するとすぐに爆発するダミー クラスです。これは予想される動作であり、 — 私が完全に間違っていなければ — 文書化されています。

于 2011-12-26T21:13:34.550 に答える
7

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

于 2012-08-18T13:58:45.873 に答える
4

iOSのバグであることが判明しました。Apple から連絡があり、iOS 6 でこれを修正したとのことでした。

于 2012-07-13T18:33:04.927 に答える
0

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

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

于 2011-12-07T12:45:33.567 に答える
0

NSZombieEnabled を開いているため、オブジェクトは dealloc を呼び出さず、オブジェクトを特別な場所に配置できます。NSZombieEnabled を閉じて、もう一度やり直してください。コードに円保持条件があるかどうかを再確認してください。

于 2012-05-18T18:42:27.027 に答える