【发布时间】:2010-12-13 21:15:56
【问题描述】:
我一直在使用产品展示应用程序,但它存在内存泄漏,导致加载过多类别后崩溃。该应用程序通过 SplitViewController 工作,该控制器在左侧列出类别,一旦点击,产品图像就会显示在右侧的 detailViewController 中。 选择一个又一个类别最终会使应用程序崩溃。
我使用了instruments -> Leaks 工具来追踪问题,我被告知NSString appendString 是一个泄漏。泄漏的字符串数量似乎与所选类别中的产品数量相匹配,所以我猜我的一个循环存在问题,但是在使用 AutoreleasePools 之后我还没有解决它。
我的代码: 选择类别并解析 XML 文档时调用此方法
- (NSMutableArray*) processXML{
//NSAutoreleasePool *pool4 = [[NSAutoreleasePool alloc] init];
// Initialize the productEntries MutableArray declared in the header
products = [[NSMutableArray alloc] init];
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSMutableString *documentsDirectory = [[NSMutableString stringWithFormat:@"%@", [paths objectAtIndex: 0]] autorelease];
// paths to save inputs to
NSString *productsFile = [documentsDirectory stringByAppendingFormat: @"/products2.xml"];
NSData *data = [NSData dataWithContentsOfFile: productsFile];
// Create a new rssParser object based on the TouchXML "CXMLDocument" class, this is the object that actually grabs and processes the RSS data
NSError *error = nil;
CXMLDocument *rssParser = [[[CXMLDocument alloc] initWithData:(NSData *)data encoding:NSUTF8StringEncoding options:0 error:&error] autorelease];
// Create a new Array object to be used with the looping of the results from the rssParser
NSArray *resultNodes = NULL;
//NSString *xPathStart, *xPathEnd, *category, *finalStr;
NSString *xPathStart = [[NSString stringWithFormat:@""] autorelease];
NSString *xPathEnd = [[NSString stringWithFormat:@""] autorelease];
NSString *category = [[NSString stringWithFormat:@""] autorelease];
NSString *finalStr = [[NSString stringWithFormat:@""] autorelease];
NSString *detailStr = [[NSString stringWithFormat: detailItem] autorelease];
// category to be parsed - build up xPath expression
if([detailStr isEqualToString: @"On Order Stock"]) {
xPathStart = @"/products/product[instock='2";
xPathEnd = @"']";
finalStr = [NSString stringWithFormat:@"%@%@", xPathStart, xPathEnd];
} else {
xPathStart = @"/products/product[category='";
category = detailItem;
xPathEnd = @"']";
finalStr = [NSString stringWithFormat:@"%@%@%@", xPathStart, category, xPathEnd];
}
resultNodes = [rssParser nodesForXPath: finalStr error:nil];
// Loop through the resultNodes to access each items actual data
for (CXMLElement *resultElement in resultNodes) {
Product *productItem = [[Product alloc] init];
[productItem setCode: [[[resultElement childAtIndex: 1] stringValue] autorelease]];
[productItem setImage: [[[resultElement childAtIndex: 5] stringValue] autorelease]];
// Add the product object to the global productEntries Array so that the view can access it.
[products addObject: productItem];
[productItem release];
}
//[pool4 release];
return products;
}
如您所见,我对我的琴弦上的 autoReealse 有点疯狂。显示图像的其他代码段可能是问题所在,尽管 Leaks 确实直接提到了 processXML。
- (void) displayImages:(NSMutableArray *)anArray {
// create scrollView object
scrollView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height - 100)];
scrollView.pagingEnabled = NO;
scrollView.scrollEnabled = YES;
scrollView.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1];
scrollView.userInteractionEnabled = YES;
//create info area below scrollView
infoView = [[UIScrollView alloc] initWithFrame:CGRectMake(0, self.view.frame.size.height - 100, self.view.frame.size.width, 100)];
[infoView setContentSize:CGSizeMake(self.view.frame.size.width, 100)];
infoView.backgroundColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
infoView.scrollEnabled = NO;
[barcodeImgView setImage:[UIImage imageNamed:@"barcode2.jpg"]];
[infoView addSubview:codeLbl];
[infoView addSubview:nameLbl];
[infoView addSubview:priceLbl];
[infoView addSubview:dimensionsLbl];
[infoView addSubview:stockLbl];
[infoView addSubview:commentsLbl];
[infoView addSubview:barcodeImgView];
infoView.userInteractionEnabled = YES;
[codeLbl setText:[[NSString stringWithFormat:@""] autorelease]];
[nameLbl setText:[[NSString stringWithFormat:@""] autorelease]];
[priceLbl setText:[[NSString stringWithFormat:@""] autorelease]];
[commentsLbl setText:[[NSString stringWithFormat:@""] autorelease]];
[stockLbl setText:[[NSString stringWithFormat:@""] autorelease]];
[dimensionsLbl setText:[[NSString stringWithFormat:@""] autorelease]];
// hold x and y of each image
int x = 30;
int y = 50;
int noOfImages = [anArray count];
int maxRowWidth = (noOfImages / 3) + 1;
int xcount = 0; // position across the row, reset to zero and drop image down when equal to (noOfImages / 3) + 1
//NSAutoreleasePool *displayPool = [[NSAutoreleasePool alloc] init];
for(int i = 0; i < noOfImages; i++) {
// declare Product object to hold items in anArray
Product *prod = [[Product alloc] init];
prod = [anArray objectAtIndex: i];
// try for image in Documents folder, later checks it exists and if not uses Resource location
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSMutableString *documentsDirectory = [[NSString stringWithFormat:@"%@", [paths objectAtIndex: 0]] autorelease];;
// paths to save inputs to
NSString *imgName = [[NSString stringWithFormat:@"%@/%@", documentsDirectory, [prod image]] autorelease];
NSString *productName = [[NSString stringWithFormat:@"%@", [prod code]] autorelease];
// create and size image
UIImage *image = [UIImage imageWithContentsOfFile: imgName];
// set up button
UIButton *button= [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button addTarget:self action:@selector(imageButtonClick:) forControlEvents:(UIControlEvents)UIControlEventTouchDown];
[button setTitle:productName forState:UIControlStateNormal];
button.titleLabel.font = [UIFont systemFontOfSize: 0];
[button setTitleColor: [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1] forState: UIControlStateNormal];
CGSize imageSize = image.size;
CGFloat height = imageSize.height;
CGFloat width = imageSize.width;
CGFloat ratio = 160 / width; // get ratio to divide height by
UIGraphicsBeginImageContext(CGSizeMake((height * ratio),160));
CGContextRef context = UIGraphicsGetCurrentContext();
[image drawInRect: CGRectMake(0, 0, height * ratio, 160)];
UIImage *smallImage = UIGraphicsGetImageFromCurrentImageContext();
UIGraphicsEndImageContext();
// create frame for image
CGRect newFrame = CGRectMake(x, y, 160,160);
UILabel *codeLabel = [[UILabel alloc] initWithFrame:CGRectMake(x, y - 20, 170, 20)];
codeLabel.text = productName;
codeLabel.textColor = [UIColor colorWithRed:0.1 green:0.1 blue:0.1 alpha:1];
codeLabel.backgroundColor = [UIColor colorWithRed:0.9 green:0.9 blue:0.9 alpha:1];
[button setFrame: newFrame];
[button setBackgroundImage:smallImage forState:UIControlStateNormal];
[scrollView setContentSize:CGSizeMake((maxRowWidth * 160) + 160,self.view.frame.size.height - 100)];
[self.scrollView addSubview:button];
[self.scrollView addSubview:codeLabel];
xcount++;
x = x + 170; // move across the page
if(xcount == maxRowWidth) {
y = y + 210; // move down the screen for the next row
x = 30; // reset x to left of screen
xcount = 0; // reset xcount;
}
[prod release];
}
//[displayPool release];
[self.view addSubview: scrollView];
[self.view addSubview: infoView];
[scrollView release];
[infoView release];
[pool release];
}
顺便说一下,pool 是在 h 文件中为类定义的 autoreleasePool。
非常感谢有关我的代码的任何具体帮助或关于可能出错的一般提示。
【问题讨论】:
-
[[NSString stringWithFormat:@""] autorelease];不要这样做。您过度释放这些对象,它们会使您的程序崩溃 . -
您的代码不包含对
appendString:的任何调用,您声称这是泄漏源。此外,这不会导致泄漏,但所有这些[[NSString stringWithFormat:whatever] autorelease]行都是绝对错误的,很可能会导致崩溃。您不拥有该字符串,因此您不能释放它。 developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/…
标签: objective-c ipad memory-leaks