1

メインスレッドでのみ次を実行すると、irefすぐに自動解放されます。

-(void)loadImage:(ALAsset*)asset{
    @autoreleasepool {
        ALAssetRepresentation* rep = [asset defaultRepresentation];
        CGImageRef iref = [rep fullScreenImage];
        UIImage* image = [UIImage imageWithCGImage:iref
                                             scale:[rep scale]
                                       orientation:UIImageOrientationUp];

        [self.imageView setImage:image];
    }
}

しかし、imageWithCGImage: with GCD をバックグラウンド スレッドで実行するirefと、最初の例のようにすぐに解放されません。約1分後:

-(void)loadImage:(ALAsset*)asset{

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
        @autoreleasepool {
            ALAssetRepresentation* rep = [asset defaultRepresentation];
            CGImageRef iref = [rep fullScreenImage];
            UIImage* image = [UIImage imageWithCGImage:iref
                                                 scale:[rep scale]
                                           orientation:UIImageOrientationUp];

            dispatch_async(dispatch_get_main_queue(), ^(void) {
                [self.imageView setImage:image];
            });
        }
    });
}

CGImageRefオブジェクトをすぐに解放するにはどうすればよいですか?

先行研究:

  • Leak インストゥルメントで録音すると、リークが表示されません。
  • Allocations インストゥルメントは、CGImageRefオブジェクトが割り当てられ、解放されるべきだった後も約 1 分間はまだ生きていることを示しています。
  • CGImageRefを使用してオブジェクトを手動で解放CGImageReleaseしようとすると、イメージが自動解放されようとする 1 分後に BAD_EXEC 例外が発生します。
  • iref使用を保持してCGImageRetainから使用CGImageReleaseして解放することはできません。
  • 役に立たなかったスタックオーバーフローに関する同様の質問: gcd を使用した画像の読み込み、 create imageでのメモリ警告の受信、alaseet result から fullscreenimage を取得するときのメモリリーク
4

2 に答える 2

10

まず、「自動解放された」CF オブジェクトという概念はありません。フリーダイヤルのブリッジ クラスを扱う場合、そのようなことが存在する状況に陥ることがありますが、ご覧のとおり、 と はありますが、 はありCFRetainませCFReleaseCFAutorelease。の所有権を誤解していると思いますiref。このコード全体で所有権を追跡しましょう。

-(void)loadImage:(ALAsset*)asset{

assetこのメソッドに渡されます。その保持カウントは少なくとも 1 であると想定されます。

    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {

ブロック クロージャは保持を受け取りますasset

        @autoreleasepool {
            ALAssetRepresentation* rep = [asset defaultRepresentation];

これは、命名規則により、所有していないオブジェクトを返します。自動解放されている可能性があり、シングルトン/グローバルなどである可能性があります。ただし、所有していないため、保持して所有権を取得しないでください。

            CGImageRef iref = [rep fullScreenImage];

「自動解放された」CF オブジェクトの概念がないため、が所有するrepへの内部ポインターを返していると想定します。また、これを所有していないため、保持しないでください。関連して、いつ消えるかは制御できません。妥当な推測では、それは の期間だけ存続し、妥当な推測では の期間は存続します。CGImageRefrepreprepassetirefasset

            UIImage* image = [UIImage imageWithCGImage:iref
                                                 scale:[rep scale]
                                           orientation:UIImageOrientationUp];

UIImage が CGImageRef を保持する必要がある場合は、維持するかコピーを作成して、確実に存続させます。(おそらく後者です。) UIImage 自体は、命名規則により自動解放されます。

            dispatch_async(dispatch_get_main_queue(), ^(void) {

この内側のブロック クロージャは、image(and self) を保持します。ブロックは libdispatch によってコピーされ、ブロックが実行されてそれ自体が解放されるまで、保持期間が延長されます。

                [self.imageView setImage:image];

image画像ビューは、必要に応じて、ジョブを実行するために保持 (またはコピー)を実行します。

            });

内側のブロックの実行が完了しました。将来のある時点で、libdispatch はそれを解放します。これにより、 および のブロック クロージャーによって取得された保持が推移的に解放されselfますimage

        }

自動解放プールがここに表示されます。暗黙的に保持/自動解放されたものはすべて、今すぐ解放する必要があります。

    });

外側のブロックの実行が完了しました。将来のある時点で、libdispatch はそれを解放します。これにより、 のブロック クロージャーによって取得された保持が推移的に解放されassetます。

}

最終的に、このメソッドは CGImageRef の所有権を持たないため、CGImageRef の有効期間を制御できませんiref。ここでの含意は、CGImageRef が によって推移的に所有されassetているため、少なくともasset. は外側のブロックで使用されているためasset保持されている (つまり、外側のブロックのクロージャーによって保持されている) ため、libdispatch は完成したブロックがいつ解放されるかについて約束をしないため、事実上iref、libdispatch が取得するよりも早くなくなることを保証できません。そのあたり。

手動での保持/解放を行い、それについてできるだけ明確にしたい場合は、次のようにすることができます。

-(void)loadImage:(ALAsset*)asset{
    __block ALAsset* weakAsset = [asset retain]; // asset +1
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) {
        @autoreleasepool {
            ALAssetRepresentation* rep = [weakAsset defaultRepresentation];
            CGImageRef iref = [rep fullScreenImage];
            UIImage* image = [[UIImage alloc] imageWithCGImage:iref
                                                 scale:[rep scale]
                                           orientation:UIImageOrientationUp];

            __block UIImage* weakImage = [image retain]; // image +1

            [weakAsset release]; // asset -1

            dispatch_async(dispatch_get_main_queue(), ^(void) {
                [self.imageView setImage: weakImage];
                [weakImage release]; // image -1
            });
        }
    });
}

__blockブロッククロージャーが保持さassetimageないようにし、明示的に保持/解放できるようにします。これは、作成したすべてのもののうち、すべてが明示的に破棄されることを意味します。(repそしてimageおそらく保持/自動解放されますが、プールはそれらを処理する必要があります。)これがあなたに渡されることを考えると、これが最も明確であるassetと思います。したがって、それがどれだけ存続するかを制御しませんであり、最終的には、(このスコープに関する限り) に格納されている CGImageRef の「所有者」irefです。

物事が少し明確になることを願っています。

于 2013-10-23T15:22:37.593 に答える
2

CGImageRef を保持する必要があり
ます ..

CGImageRef iref = CGImageRetain([rep fullScreenImage]);
//..before [self.imageView..
CGImageRelease(iref)

残りは単なる実行ループの問題です.1つはGCDなしでイメージが即時にリリースされ、もう1つはGCDによって管理されますが、とにかく間違っています.誰かがirefの所有権を取得する必要があります.

于 2013-10-23T12:29:15.307 に答える