我试图找出为什么我的应用程序使用大量内存然后在几次内存警告后崩溃。仪器VM跟踪器显示它使用〜30Mb作为脏内存。分配显示10-15 Mb并不是那么多。 一旦应用程序处理显示大量图像(缩略图),我认为图像是我应该看的。 我没有使用标准的UIImage imageNamed方法,而是使用imageWithData进行缓存。当系统发送内存警告时,我清理缓存存储。为了确保我创建的图像在不再需要时被销毁,我将UIImage子类化并覆盖了imageWithData,release和dealloc方法。我可以看到调用了imageWithData但是从不调用release和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
我的缓存代码:
·H
#import "BaseUIimage.h"
@interface UIImageCached : BaseUIimage
// CACHE
+ (NSMutableDictionary*) cache;
+ (void) cleanCache;
+ (UIImageCached *)imageNamed:(NSString *)imageName;
+ (UIImageCached *)retinaImageNamed:(NSString *)imageName;
+ (UIImageCached *)imageFromPath:(NSString *)imagePath;
的.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 :(得分:4)
Dave Wood几乎是正确的(他的实现仍将创建一个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。不是你需要的。我建议以下实施:
+ (id)imageWithData:(NSData *)data {
NSLog(@"UIImage imageWithData");
BaseUIimage *im = [[BaseUIimage alloc] initWithData:data];
return [im autorelease];
}
这将创建BaseUIimage类的图像,从而表现出您的期望。干杯。
答案 1 :(得分:3)
您创建的子类错误。在这里的代码中:
@implementation BaseUIimage
+ (id)imageWithData:(NSData *)data {
NSLog(@"UIImage imageWithData");
return [UIImage imageWithData:data];
}
...
你没有得到BaseUIimage的实例,你得到一个常规的UIImage,这意味着你的被覆盖的release / dealloc等没有被调用,因为它们不是UIImage类的一部分。
您需要将该功能更改为:
@implementation BaseUIimage
+ (id)imageWithData:(NSData *)data {
NSLog(@"UIImage imageWithData");
return [super imageWithData:data];
}
...
这将返回您的BaseUIimage类的实例。现在,您将能够看到被覆盖的方法被调用。