Je travaille sur une application d'affichage de produit, mais il y a une fuite de mémoire qui provoque son arrêt brutal après que trop de catégories ont été chargées. L'application fonctionne via un SplitViewController qui liste les catégories à gauche et, une fois tapées, les images du produit s'affichent dans le detailViewController sur la droite. La sélection de la catégorie après la catégorie finit par bloquer l'application.fuite de mémoire dans l'application iPad
J'ai utilisé l'outil instruments -> Leaks pour tracer le problème et on me dit que NSString appendString est une fuite. Le nombre de chaînes fuites semble correspondre au nombre de produits dans la catégorie sélectionnée, donc je suppose que l'une de mes boucles contient le problème mais, après avoir joué avec AutoreleasePools, je ne l'ai pas encore résolu.
Mon code: Cette méthode est appelée lorsque la catégorie est sélectionnée et parse un document 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;
}
Comme vous pouvez le voir, je suis un peu fou avec autoReealse sur mes cordes. L'autre segment de code qui affiche les images pourrait être le problème, bien que Leaks mentionne directement 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];
}
Par ailleurs, la piscine est un autoreleasePool défini dans le fichier h pour la classe.
J'apprécierais vraiment toute aide spécifique concernant mon code ou des conseils généraux sur ce qui pourrait être faux.
'. [[NSString stringWithFormat: @ ""] autorelease];' *** NE PAS FAIRE CE *** Vous overreleasing ces objets et ils vont * planter votre programme *. –
Votre code n'inclut aucun appel à 'appendString:', qui selon vous, est identifié comme la source de la fuite. De plus, cela ne causera pas de fuite, mais toutes ces lignes [[NSString stringWithFormat: whatever] autorelease] 'sont absolument fausses et provoqueront probablement un crash. Vous n'êtes pas propriétaire de la chaîne, vous ne devez donc pas la libérer. http://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html – Chuck