2010-12-13 54 views
0

我一直在使用产品展示应用程序,但它有一个内存泄漏,导致它在加载了太多类别后崩溃。该应用程序通过一个SplitViewController工作,该工具列出左侧的类别,一旦点击,产品图像显示在右侧的detailViewController中。 选择类别后的类别最终会使应用程序崩溃。iPad应用程序中的内存泄漏

我用过仪器 - > 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]; 

}

顺便说,池在H文件的类中定义的autoreleasePool。

我真的很感激任何关于我的代码的具体帮助或关于可能出错的一般提示。

+4

'[[的NSString stringWithFormat:@ “”]自动释放]'*** DO NOT DO THIS ***你overreleasing这些对象和他们会*你的程序崩溃*。 – 2010-12-13 21:20:53

+0

您的代码不包含任何对'appendString:'的调用,您声明该调用被标识为泄漏源。此外,这不会导致泄漏,但所有这些'[[NSString stringWithFormat:无论] autorelease]'行是绝对错误的,很可能会导致崩溃。你不拥有这个字符串,所以你不能释放它。 http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html – Chuck 2010-12-13 21:27:09

回答

2

我看到几件事情错了:

  1. 正如在评论中提到,你的方式,这使得成年男子哭泣,并且这会崩溃您的应用程序滥用-autorelease
  2. -processXML正在返回一个拥有的对象。您正在分配products并将其返回。这违反了约定,因为方法名称不以newalloc开头,也不包含copy。您应该改为return [products autorelease];。然而,即使这是阴暗的,因为products没有在本地声明,它可能是一个实例变量。在那种情况下,如果processXML被多次调用会发生什么?你有一个由实例变量引用的拥有对象,并突然用新的引用覆盖该引用。
  3. 每当有人这样做MyClass * object = [[MyClass alloc] init]; object = [something thatReturnsAMyClass];,一只小猫死亡。如果你接着做[object release];,那么第二个就会死亡。这是一个可怕的,可怕的内存泄漏(并可能崩溃)。您正在分配一个新对象,然后立即抛弃它,但从未释放它。你这样做表明你并没有真正得到什么指针。我建议你阅读“Everything you need to know about pointers in C
  4. 在一个较轻的笔记上,你应该检查出-[NSString stringByAppendingPathComponent:]NSString有一堆非常好的处理路径的方法。

我希望我不要太苛刻。 :)

+0

不要太苛刻,戴夫。我知道我滥用autorelease,它确实违背了我在文档中读到的内容,但无法追踪appendString泄漏,我越来越绝望。我修改了产品对象来修复它的一面。仍然在寻找这些appendString。 – Steve 2010-12-14 04:12:12

1

前些时间在另一篇文章中,有人说应该阅读关于内存管理的内容,而实际上我认为这个答案并不是真的。一些试验和错误以及边干边学都有什么问题。但是,在我记忆痛苦的经历之后,我必须承认这个人是对的。慢慢来。去阅读苹果文档中有关内存管理的章节。

如上所述,您不应该自动释放您不属于的对象。但这可能不会造成麻烦。您可以在生成菜单中使用Build + Analyze旁边的乐器。这将帮助您了解更多信息。

基本上你需要释放你自己创建的对象(你拥有的对象在文档中,基本上是用“alloc”等创建的)。如果你不能释放它们,你可以将它们分配给自动释放池。您从processXML返回的“产品”就是这种情况。自动释放池何时耗尽?这是下一次应用程序的框架重新控制时(我认为它被称为运行循环或其他)。这可能是一段时间,所以你不应该打开分配给自动释放池的很多对象。

因此,为了帮助你真正读懂这一章:memory management programming guide