0

NSAutoreleasePool を設定したスレッド メソッドから戻ると、EXC_BAD_ACCESS エラーが発生します。失敗の場所は、NSRunLoop への呼び出しにあります。主にクラス(接続クラスと呼びましょう)とそのデリゲートで構成されるサードパーティライブラリをラップして、クライアントクラスに対して非同期ではなく同期的に動作するようにしようとしています。NFCConnection と呼ばれるラッピング クラスはデリゲート プロトコルに準拠し、NFCConnection のコンストラクターで Connection の setDelegate メソッドに渡されます。

新しいスレッドを作成する方法は次のとおりです。

- (void)methodA {
    [NSThread detachNewThreadSelector:@selector(doSomethingImportant) 
                             toTarget:self 
                           withObject:nil];

    while (!callBackInvoked) {
        [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode     // Error!
                                 beforeDate:[NSDate dateWithTimeIntervalSinceNow:1.0]];        
    }
}

スレッド メソッド doSomethingImportant は次のようになります。

- (void)doSomethingImportant {
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];    
    [[Connection instance] somethingImportant];    
    [pool release];
}

Connection の somethingImportant を呼び出すと、デリゲート メソッドの 1 つがメイン スレッドで呼び出されます。これらのコールバック メソッドのそれぞれで、変数 callBackMethod を NO に設定し、methodA の while ループを終了させます。エラーは、doSomethingImportant が返された後、デリゲート メソッドの 1 つが呼び出される前に発生します。EXC_BAD_ACCESS エラーは、NSRunLoop の呼び出しで発生します。スタック トレースの一部を次に示します。

#0  0x3138cec0 in objc_msgSend
#1  0x000100ac in -[Connection ProcessRx_Api_SomethingImportant:] at Connection.m:621
#2  0x000114fa in +[Connection ProcessRx:] at Connection.m:900
#3  0x00012758 in -[CommHandling ProcessRx:length:] at CommHandling.m:294
#4  0x000126b8 in -[CommHandling stream:handleEvent:] at CommHandling.m:331
#5  0x30a7b958 in -[EAInputStream _streamEventTrigger]
#6  0x30a7be78 in __streamEventTrigger
#7  0x323f53a6 in CFRunLoopRunSpecific
#8  0x323f4c1e in CFRunLoopRunInMode
#9  0x3373c966 in -[NSRunLoop(NSRunLoop) runMode:beforeDate:]
#10 0x0000ae66 in -[NFCConnection methodA:] at NFCConnection.m:137
#11 0x0000bbf2 in -[NFCTestViewController doIt] at NFCTestViewController.m:39
...

これで、エラーの発生をまったく防ぐことができ、doSomethingImportant で自動解放プールの設定を怠ると、ラップされた API が同期的に動作するように見えます。しかし、それを行うと、コンソールに次のように出力されます。

2010-08-09 14:54:49.259 ConnetionTest[3353:652f] *** _NSAutoreleaseNoPool(): Object 0x1928b0 of class __NSCFDate autoreleased with no pool in place - just leaking
Stack: (0x3374ff83 0x33723973 0x3372393f 0x323f78f1 0x3372b913 0x10221 0xc833 0xb045 0x33731acd 0x336dfd15 0x33ad8788)
2010-08-09 14:54:49.272 ConnetionTest[3353:652f] *** _NSAutoreleaseNoPool(): Object 0x18f800 of class NSCFTimer autoreleased with no pool in place - just leaking
Stack: (0x3374ff83 0x33723973 0x3372393f 0x3372b93b 0x10221 0xc833 0xb045 0x33731acd 0x336dfd15 0x33ad8788)

上記のメッセージは、Connection クラスのインスタンスで何かが解放されていないことが原因ですか? 私は NSDate を作成していますが、リリースする必要があるインスタンスではありません。NSTimer と同じことです。

ここで NSAutoreleasePool を設定して正しいことをしようとしていますが、何か間違っているようです。しかし、私はそれが何であるかについての手がかりを持っていません。どんな助けでも大歓迎です!

リッチ

4

1 に答える 1

1

まず、新しいスレッドに自動解放プールが必要であることは間違いないので、これを解決しようとはしないでください。:-)

とは言うものの、これには時期尚早のリリースまたはコードのどこかに過剰リリースの特徴がいくつかあります。デリゲートを保持するオブジェクトに関する規則はやや滑りやすいため、Connection オブジェクトのデリゲートに特に注意を払います。(通常、Cocoa オブジェクトはデリゲートを保持しませが、サードパーティのコードはデリゲートを保持する可能性があり、正当な理由がある場合もあります。)

Tracking Memory Usageに注目してください。MallocDebug と NSZombieEnabled=YES のいくつかの置換により、最終的に犯人コードが明らかになるはずです。

ただし、このバグを過ぎたら、独自のスレッドをロールするのではなく、この種のことについて Grand Central Dispatch を調べたいと思うかもしれません...現状のコードは、おそらく NSCondition または pthread_condition 変数を使用して厳密に正しい。現在のハードウェアでは多くのことを回避できますが、この種の同期されていない共有アクセスは、本当に厄介な競合を簡単に引き起こす可能性があります。GCD (別名 libdispatch) は、はるかにクリーンで最新のパラダイムを提供するため、何か新しいことを学ぶ場合は、pthreads / NSThread / などよりもはるかに優れた投資になります:-)

于 2010-08-09T22:57:41.907 に答える