0

UIViewController があり、そのコントローラーで、URL ソースから画像を取得しています。画像は別のスレッドでフェッチされ、その後メイン スレッドでユーザー インターフェイスが更新されます。このコントローラーは、表示されなくなったコントローラーを解放するために実装された UIScrollView 親のページとして表示されます。

UIViewController が解放される前にスレッドがコンテンツのフェッチを終了すると、すべて正常に動作しますが、スレッドが終了する前にユーザーが別のページにスクロールすると、コントローラーが解放され、コントローラーへの唯一のハンドルがコントローラーの releaseCount を作成するスレッドによって所有されます。これで、スレッドが NSAutoreleasePool を排出するとすぐに、releaseCount が 0 になるため、コントローラーが解放されます。この時点で、アプリケーションがクラッシュし、次のエラー メッセージが表示されます。

bool _WebTryThreadLock(bool), 0x4d99c60: メイン スレッドまたは Web スレッド以外のスレッドから Web ロックを取得しようとしました。これは、セカンダリ スレッドから UIKit を呼び出した結果である可能性があります。現在クラッシュ中...

バックトレースは、[super dealloc] の呼び出しでアプリケーションがクラッシュしたことを示しています。これは、プールがドレインされたときにスレッドによって dealloc 関数がトリガーされたに違いないため、完全に理にかなっています。私の質問は、このエラーを克服し、メモリをリークすることなくコントローラーを解放するにはどうすればよいですか?

私が試した解決策の 1 つは、プールが空になる前に [自己保持] を呼び出して、retainCount がゼロにならないようにし、次のコードを使用してメイン スレッドでコントローラーを解放することでした。

[self performSelectorOnMainThread:@selector(autorelease) 
     withObject:nil waitUntilDone:NO];

残念ながら、これはうまくいきませんでした。以下は、スレッドで実行される関数です。

- (void)thread_fetchContent {

    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    NSURL *imgURL = [NSURL URLWithString:@"http://www.domain.com/image.png"];

    // UIImage *imgHotspot is declared as private - The image is retained 
    // here and released as soon as it is assigned to UIImageView

    imgHotspot = [[[UIImage alloc] initWithData:
         [NSData dataWithContentsOfURL: imgURL]] retain];

    if ([self retainCount] == 1) {

        [self retain]; // increment retain count ~ workaround
        [pool drain]; // drain pool

        // this doesn't work - i get the same error

        [self performSelectorOnMainThread:@selector(autorelease)
             withObject:nil waitUntilDone:NO];
    }

    else {

        // show fetched image on the main thread - this works fine!

        [self performSelectorOnMainThread:@selector(showImage)
             withObject:nil waitUntilDone:NO];

        [pool drain];
    }
}

助けてください!前もって感謝します。

4

1 に答える 1

0

ええ、スレッド化されたものを同期させようとするのは本当に大変なことです。あなたが説明したユースケースは、NSOperationにぴったりです。このアプローチを使用すると、NSOperationQueue をコントローラーの ivar として保持し、コントローラーの dealloc メソッドでこれを解放できます。

コントローラー ビューが scrollView に表示されている場合 (viewWillAppear または loadView) は、NSOperationQueue に追加された NSOperation を使用して画像の取得を開始します。操作が完了する前にユーザーがスクロールして NSOperationQueue が解放されると、多くの利点があります。すべての操作にキャンセル メッセージを送信し、通常はすべてを整然と閉じます。

これがアプリの中心的なコンポーネントである場合は、「画面の」ものを解放することを考えているためだと思います。コントローラーに loadVIew メソッドで「ダミー画像」を表示させてから、フェッチを開始することをお勧めします。 viewDidLoad での操作。NSOperation をサブクラス化して、URL を送信するだけで動作させることができます。

私は数週間前にスレッド化された操作を継続的に開始しなければならなかった同様のことをしましたが、ユーザーがこれらをキャンセルする原因となる何かを行う可能性が高くなりました。その機能は、NSOperation に「組み込まれています」。 NS操作の質問

于 2010-05-30T10:58:34.890 に答える