【发布时间】:2013-02-04 22:28:14
【问题描述】:
我遇到了一个问题,相对较大的图像似乎永远不会从内存中释放出来(大小为 1MB~5MB)。当用户滚动一组图像时,会调用以下代码块。大约 15 张图像后,应用程序将崩溃。有时会调用“didReceiveMemoryWarning”,有时不会调用——应用程序只会崩溃、停止、退出调试,并且不会在任何代码行上停止——什么都没有。我认为这是设备内存不足时会发生的情况?另一个问题是“dealloc”似乎永远不会被子类“DownloadImageOperation”调用。有什么想法吗?
获取和设置图像:
//Calling this block of code multiple times will eventually cause the
// application to crash
//Memory monitor shows real memory jumping 5MB to 20MB increments in instruments.
//Allocations tool shows #living creeping up after this method is called.
//Leaks indicate something is leaking, but in the 1 to 5 kb increments. Nothing huge.
DownloadImageOperation * imageOp = [[DownloadImageOperation alloc] initWithURL:imageURL localPath:imageFilePath];
[imageOp setCompletionBlock:^(void){
//Set the image in a UIImageView in the open UIViewController.
[self.ivScrollView setImage:imageOp.image];
}];
//Add operation to ivar NSOperationQueue
[mainImageQueue addOperation:imageOp];
[imageOp release];
下载图像操作定义:
.h 文件
#import <Foundation/Foundation.h>
@interface DownloadImageOperation : NSOperation {
UIImage * image;
NSString * downloadURL;
NSString * downloadFilename;
}
@property (retain) UIImage * image;
@property (copy) NSString * downloadURL;
@property (copy) NSString * downloadFilename;
- (id) initWithURL:(NSString *)url localPath:(NSString *)filename;
@end
.m 文件
#import "DownloadImageOperation.h"
#import "GetImage.h"
@implementation DownloadImageOperation
@synthesize image;
@synthesize downloadURL;
@synthesize downloadFilename;
- (id) initWithURL:(NSString *)url localPath:(NSString *)filename {
self = [super init];
if (self!= nil) {
[self setDownloadURL:url];
[self setDownloadFilename:filename];
[self setQueuePriority:NSOperationQueuePriorityHigh];
}
return self;
}
- (void)dealloc { //This never seems to get called?
[downloadURL release], downloadURL = nil;
[downloadFilename release], downloadFilename = nil;
[image release], image = nil;
[super dealloc];
}
-(void)main{
if (self.isCancelled) {
return;
}
UIImage * imageProperty = [[GetImage imageWithContentsOfFile:downloadFilename andURL:downloadURL] retain];
[self setImage:imageProperty];
[imageProperty release];
imageProperty = nil;
}
@end
获取图像类
.m 文件
+ (UIImage *)imageWithContentsOfFile:(NSString *)path andURL:(NSString*)urlString foundFile:(BOOL*)fileFound {
BOOL boolRef;
UIImage *image = nil;
NSString* bundlePath = [[NSBundle mainBundle] bundlePath];
if (image==nil) {
boolRef = YES;
image = [UIImage imageWithContentsOfFile:[[AppDelegate applicationImagesDirectory] stringByAppendingPathComponent:[path lastPathComponent]]];
}
if (image==nil) {
boolRef = YES;
image = [super imageWithContentsOfFile:path];
}
if (image==nil) {
//Download image from the Internet
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:YES];
NSURL *url = [NSURL URLWithString:[urlString stringByAddingPercentEscapesUsingEncoding:NSUTF8StringEncoding]];
ASIHTTPRequest *request = [ASIHTTPRequest requestWithURL:url];
[request setTimeOutSeconds:120];
[request startSynchronous];
NSData *responseData = [[request responseData] retain];
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:NO];
NSData *rdat = [[NSData alloc] initWithData:responseData];
[responseData release];
NSError *imageDirError = nil;
NSArray *existing_images = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[path stringByDeletingLastPathComponent] error:&imageDirError];
if (existing_images == nil || [existing_images count] == 0) {
// create the image directory
[[NSFileManager defaultManager] createDirectoryAtPath:[path stringByDeletingLastPathComponent] withIntermediateDirectories:NO attributes:nil error:nil];
}
BOOL write_success = NO;
write_success = [rdat writeToFile:path atomically:YES];
if (write_success==NO) {
NSLog(@"Error writing file: %@",[path lastPathComponent]);
}
image = [UIImage imageWithData:rdat];
[rdat release];
}
return image;
}
为这个巨大的代码块道歉。我真的不知道问题可能出在哪里,所以我尽量做到包容。感谢阅读。
【问题讨论】:
-
您可以在您的项目中使用 ARC 吗?它通常可以简化内存管理。
-
很遗憾,这不是使用 ARC。这个项目是在 iOS 5 之前开始的。不过我很想找到一些方法来转换它。
-
@Erracity - 我认为转向 ARC 确实值得,即使您第一次这样做需要付出一些努力。它使您摆脱了内存管理的麻烦,并消除了许多简单的可能泄漏。您仍然需要使用我在下面向您展示的
blockImageOp技巧的__weak变体(例如__weak DownloadImageOperation *weakImageOp = imageOp;)来避免完成块中的保留循环,但是所有手动调用retain和release走开。 -
感谢您的洞察力,现在运行良好。我很可能会花一些时间来重构它以使用 ARC,特别是因为看起来 xcode 中有一个工具可以这样做
标签: ios xcode memory-management memory-leaks