2012-04-29 10 views
0

私のアプリがたくさんのメモリを使い、いくつかのメモリ警告の後にクラッシュする理由を理解しようとしています。インストゥルメントのVMトラッカーは、〜30MbをDirtyメモリとして使用していることを示しています。配分はそれほど多くない10-15Mbを示します。 アプリがたくさんの画像(サムネイル)を表示するとすぐに、私は画像を見なければならないと思います。 標準のUIImage imageNamedメソッドを使用するのではなく、imageWithDataを使用してキャッシングを行います。システムがメモリ警告を送信すると、私はキャッシュストレージをクリーニングします。それらがもはや必要でなくなったときに私が作成したイメージが破壊されていることを確認するために、私はUIImageをサブクラス化し、imageWithData、releaseおよびdeallocメソッドをオーバーライドしました。 imageWithDataが呼び出されても、releaseとdeallocは呼び出されません。UIImage deallocは決して呼ばれません

BaseUIimage.h

@interface BaseUIimage : UIImage 

@end 

BaseUIimage.m

#import "BaseUIimage.h" 

@implementation BaseUIimage 

+ (id)imageWithData:(NSData *)data { 
    NSLog(@"UIImage imageWithData"); 
    return [UIImage imageWithData:data]; 
} 

- (id)retain { 
    NSLog(@"UIImage retain: %d", [self retainCount]); 
    return [super retain]; 
} 

- (oneway void)release { 
    NSLog(@"UIImage release: %d", [self retainCount]); 
    [super release]; 
} 

- (id)autorelease { 
    NSLog(@"UIImage autorelease: %d", [self retainCount]); 
    return [super autorelease]; 
} 

- (void)dealloc { 
    NSLog(@"UIImage deallocated"); 
    [super dealloc]; 
} 

@end 

マイキャッシュコード:

#import "BaseUIimage.h" 

@interface UIImageCached : BaseUIimage 

// CACHE 
+ (NSMutableDictionary*) cache; 
+ (void) cleanCache; 
+ (UIImageCached *)imageNamed:(NSString *)imageName; 
+ (UIImageCached *)retinaImageNamed:(NSString *)imageName; 
+ (UIImageCached *)imageFromPath:(NSString *)imagePath; 

.H私はこれを行う方法です 。 m

#import "UIImageCached.h" 

@implementation UIImageCached 

static NSMutableDictionary *data; 

+ (NSMutableDictionary*) cache { 
    if (data == nil) 
     data = [[NSMutableDictionary alloc] initWithCapacity:150]; 
    return data; 
} 

+ (void) cleanCache { 
    NSLog(@"Cache cleaned images: %d", data.count); 

    for (BaseUIimage *image in data) { 
     NSLog(@"image rc: %d", [image retainCount]); // always prints rc = 1 
    } 

    [data removeAllObjects]; 
} 

+ (UIImageCached *)imageFromPath:(NSString *)imagePath { 
    UIImageCached *image = (UIImageCached*)[self.cache objectForKey:imagePath]; 
    if (image == nil) { 
     NSData *imageData = [[NSData alloc] initWithContentsOfFile:imagePath options:NSDataReadingMappedIfSafe error:nil]; 
     image = (UIImageCached*)[UIImageCached imageWithData:imageData]; 
     [imageData release]; 

     if (image) { 
      [self.cache setObject:image forKey:imagePath]; 
      //NSLog(@"new cached image: #%d", self.cache.count); 
     } else { 
      //NSLog(@"can't cache image: #%d", self.cache.count); 
     } 
    } 

    return image; 
} 

+ (UIImageCached *)imageNamed:(NSString *)imageName { 
    NSString *extension = [imageName pathExtension]; 
    NSString *fileName = [imageName stringByDeletingPathExtension]; 
    NSString *fileLocation = [[NSBundle mainBundle] pathForResource:fileName ofType:extension]; 
    return [self imageFromPath:fileLocation]; 
} 

+ (UIImageCached *)retinaImageNamed:(NSString *)imageName { 
    UIImageCached *image = (UIImageCached*)[self.cache objectForKey:imageName]; 
    if (image == nil) { 
     NSString *extension = [imageName pathExtension]; 
     NSString *fileName = [imageName stringByDeletingPathExtension]; 

     float s = 1.0; 

     // retina filename support 
     if(!isIPAD && [[UIScreen mainScreen] respondsToSelector:@selector(scale)]) { 
      s = [[UIScreen mainScreen] scale]; 
      if (s > 1) 
       fileName = NSTR2(@"%@%@", fileName, @"@2x");    
     } 

     NSString *fileLocation = [[NSBundle mainBundle] pathForResource:fileName ofType:extension]; 

     NSData *imgData = [[NSData alloc] initWithContentsOfFile:fileLocation options:NSDataReadingMappedIfSafe error:nil]; 

     BaseUIimage *tmpImage = [[BaseUIimage alloc] initWithData:imgData]; 

     [imgData release]; 

     image = (UIImageCached*)[UIImageCached imageWithCGImage:tmpImage.CGImage 
                scale:s 
               orientation:UIImageOrientationUp];   

     [tmpImage release]; 

     if (image) { 
      [self.cache setObject:image forKey:imageName]; 
      //NSLog(@"-- CACHE: new cached image: #%d", self.cache.count);   
     } else { 
      NSLog(@"-- CACHE: can't cache image: %@", fileLocation); 
     } 
    } else { 
     //NSLog(@"-- CACHE: read cached image"); 
    } 
    return image; 
} 

@end 

なぜreleaseとdeallocが呼び出されないのですか?私が作成したUIImageのインスタンスが解放されておらず、それが仮想メモリの増加の理由ですか?

+0

あなたは今までにcleanCacheを呼び出していますか?さもなければ、キャッシュはイメージを保持しているようです。 – fsaint

+0

はい。AppDelegateのこのメソッドでは、 - (void)applicationDidReceiveMemoryWarning:(UIApplication *)アプリケーション{ NSLog(@ "BTC_DashboardAppDelegate applicationDidReceiveMemoryWarning"); [UIImageCached cleanCache]; } –

答えて

3

サブクラスを間違って作成しました。ここでのコードでは:あなたがBaseUIimageのインスタンスを取得していない

@implementation BaseUIimage 

+ (id)imageWithData:(NSData *)data { 
    NSLog(@"UIImage imageWithData"); 
    return [UIImage imageWithData:data]; 
} 

... 

、あなたは彼らがの一部じゃないので、/のdeallocなどと呼ばれていない、あなたのオーバーライドされたリリースを意味し、通常のUIImageを、取得していますUIImageクラス。あなたのBaseUIimageクラスのインスタンスを返します

@implementation BaseUIimage 

+ (id)imageWithData:(NSData *)data { 
    NSLog(@"UIImage imageWithData"); 
    return [super imageWithData:data]; 
} 

... 

あなたはにその機能を変更する必要があります。これで、オーバーライドされたメソッドが呼び出されるのを見ることができます。

+2

これは確かに無限再帰をもたらします。 'は何ですか? –

+0

ええ、申し訳ありません、それは遅れています。私は代わりにスーパーを使用して答えを編集しました。 –

+0

それでも動作しません。とにかくここのスーパーはUIImageです。 –

3

デイブ・ウッドはほぼそうだった(彼の実装はまだUIImageを作成するだろう)。問題は

[UIImage imageWithData:data]; 

はUIImageではなく、あなたがそれを作成するために期待するサブクラスを作成することです。これをテストするには、imageWithDataの次の実装を試してください:

+ (id)imageWithData:(NSData *)data { 
    NSLog(@"UIImage imageWithData"); 
    UIImage *im = [UIImage imageWithData:data]; 
    NSLog(@"%@",[im class]); 
    return im; 
} 

NSLogはUIImageを出力します。あなたが必要とするものではありません。

これにより、BaseUIimageクラスのイメージが作成され、期待通りに動作するようになります。乾杯。

+1

+1、そうですね。 :) –

+0

ありがとう!以前の実装がなぜ機能していないのですか?たとえば、UIViewControllerをサブクラス化するときに動作しています。 –

+2

私はあなたがどのようにUIViewControllerサブクラスを使用するかはわかりません。しかし、[UIImage imageWithData:data]は、UIImageクラスを呼び出す場所に関係なく、UIImageクラスのオブジェクトを作成します。それのまわりではありません。あなたが[UIImage imageWithData:data]を呼び出すと、 "こんにちはUIImageクラスが私にこのデータでインスタンスを作成しています"と言っています。 UIImageはサブクラスではなくUIImageを知っています。 "imageWithData:"はクラスファクトリメソッドです。 BaseUIimageがUIImageから継承するコンストラクタを呼び出すときは、[[BaseUIimage alloc] initWithData:data]を実行するときとは異なります。問題は私の提案があなたの問題を解決したかどうかです。 – fsaint

関連する問題