iPad应用中的内存泄漏

时间:2010-12-13 21:15:56

标签: objective-c ipad memory-leaks

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

我用过这些乐器 - >泄漏工具来跟踪问题,我被告知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。

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

2 个答案:

答案 0 :(得分: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有很多非常好的方法来处理路径。
  5. 我希望我不要过于苛刻。 :)

答案 1 :(得分:1)

不久前在另一篇文章中,有人说应该读一下内存管理,我实际上认为这个答案并不正确。一些反复试验和边做边学有什么问题。但在我记忆中的痛苦经历后,我必须承认这个人是对的。慢慢来。请阅读Apple文档中有关内存管理的章节。

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

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

所以,为了帮助你真正阅读那一章:memory management programming guide