0

かなりの数のタスクをシリアル キューにディスパッチする iOS アプリに取り組んでいます。タスクは、Web サーバーから画像をダウンロードし、ディスクに保存して、後で に表示することUIImageViewです。ただし、[NSURLConnection sendAsynchrousRequest]iOS がプロセスを強制終了するまで、ますます多くのメモリを消費し続けます。

ダウンローダーの方法は次のようになります。

// dispatch_queue_t is created once by: m_pRequestQueue = dispatch_queue_create( "mynamespace.app", DISPATCH_QUEUE_SERIAL);

- (void) downloadImageInBackgroundWithURL:(NSString*) szUrl {
__block typeof(self) bSelf = self;
__block typeof(m_pUrlRequestQueue) bpUrlRequestQueue = m_pRequestQueue;

dispatch_async( m_pRequestQueue, ^{ 
    NSAutoreleasePool *pAutoreleasePool = [[NSAutoreleasePool alloc] init];
    NSURLRequest *pRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:szUrl]
                                              cachePolicy:NSURLRequestReloadIgnoringCacheData
                                          timeoutInterval:URL_REQUEST_TIMEOUT];

    [NSURLConnection sendAsynchronousRequest:pRequest queue:bpUrlRequestQueue completionHandler:^(NSURLResponse *pResponse, NSData *pData, NSError *pError) {
        NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
        if ( pError != nil ) {
        } else {                
            // convert image to png format
            UIImage *pImg = [UIImage imageWithData:pData];
            NSData *pDataPng = UIImagePNGRepresentation(pImg);
            bool bSaved = [[NSFileManager defaultManager] createFileAtPath:szCacheFile contents:pDataPng attributes:nil];                    
        }

            __block typeof(pDataPng) bpDataPng = pDataPng;
            __block typeof(pError) bpError = pError;
            dispatch_sync( dispatch_get_main_queue(), ^ {
                NSAutoreleasePool *autoreleasepool = [[NSAutoreleasePool alloc] init];
                UIImage *pImage = [[UIImage alloc] initWithData:bpDataPng];

                // display the image

                [pImage release];
//                    NSLog( @"image retain count: %d", [pImage retainCount] ); // 0, bad access

                [autoreleasepool drain];
            });
        }
        [pPool drain];
    }]; // end sendAsynchronousRequest

    [pAutoreleasePool drain];

}); // end dispatch_async
} // end downloadImageInBackgroundWithURL

[NSURLConnection sendAsynchronousRequest]プロファイラーが関数がすべてのメモリを消費するものであることを示しているので、それは内部にあると確信しています...

ただし、dispatch_*** とブロックのことについてもよくわかりません。以前は常に pthread で C および C++ コードを使用していましたが、スレッドからの移行に関する Apple のドキュメントを読んだ後、GCD を試してみることにしました。 、objective-c はとても面倒で、and をリリースするたびにクラッシュするので、解放する方法がわかりませNSData *pDataNSURLResponse *pResponse

アドバイスをお願いします...客観的なCを学び、理解するために本当に助けが必要です...

プロファイラ

追加の編集:

@robhayward のおかげで、pImg と pDataPng を __block 変数として外部に配置し、RHCacheImageView によるデータのダウンロード方法 ( NSData initWithContentOfURL ) を使用しました。

@JorisKluivers のおかげで、UIImageView が jpg と png 形式の両方を認識したので、最初の UIImage を実際に再利用して表示することができます。後で処理するには png 形式が必要であり、後で必要なときにディスクから読み取っています。

4

2 に答える 2

2

まず、作成している画像とデータ オブジェクトに落とし込みます。

UIImage *pImg = [UIImage imageWithData:pData];
NSData *pDataPng = UIImagePNGRepresentation(pImg);

おそらく別のスレッドで作成/解放されているため、おそらくブロックの外に置いてください。

__block UIImage *pImg = nil;
__block NSData *pDataPng = nil;
[NSURLConnection sendAsynchronousRequest..

(可能であれば、ARC の使用も検討してください)

この問題なしで同様の仕事をするGithubのコードがいくつかあります。お気軽にチェックしてください。

https://github.com/robinhayward/RHCache/blob/master/RHCache/RHCache/Helpers/UIImageView/RHCacheImageView.m

于 2013-01-22T14:50:43.373 に答える
2

まず、コードを単純化してみてください。私がしたこと:

  • 外側の dispatch_async を削除します。これは必要ありませんsendAsynchronousRequest。すでに非同期です。__blockこれにより、キューに別の変数が必要になることもなくなります。
  • pImg受け取った から名前を付けた画像を作成し、それを png 型pDataに変換し、後でそれから別の画像を再度作成します。何度も変換する代わりに、最初の画像を再利用します。元のファイルをディスクに書き込むこともできます (実際にディスク上に png 形式が必要な場合を除きます)。NSDatapImagepData

以下のコードは自分でコンパイルしたものではないため、いくつかの間違いが含まれている可能性があります。しかし、リークの解決に役立つ可能性があるのは、より単純なバージョンです。

- (void) downloadImageInBackgroundWithURL:(NSString*)szUrl
{
    __block typeof(self) bSelf = self;

    NSAutoreleasePool *pAutoreleasePool = [[NSAutoreleasePool alloc] init];
    NSURLRequest *pRequest = [NSURLRequest requestWithURL:[NSURL URLWithString:szUrl]
                                          cachePolicy:NSURLRequestReloadIgnoringCacheData
                                      timeoutInterval:URL_REQUEST_TIMEOUT];

    [NSURLConnection sendAsynchronousRequest:pRequest queue:m_pRequestQueue completionHandler:^(NSURLResponse *pResponse, NSData *pData, NSError *pError) {
        NSAutoreleasePool *pPool = [[NSAutoreleasePool alloc] init];
        if (pError) {
            // TODO: handle error
            return;
        }

        // convert image to png format
        __block UIImage *pImg = [UIImage imageWithData:pData];

        // possibly just write pData to disk
        NSData *pDataPng = UIImagePNGRepresentation(pImg);
        bool bSaved = [[NSFileManager defaultManager] createFileAtPath:szCacheFile contents:pDataPng attributes:nil];                    

        dispatch_sync( dispatch_get_main_queue(), ^ {
            // display the image in var pImg
        });
    }];
    [pAutoreleasePool drain];
}
于 2013-01-22T15:32:31.970 に答える