NSOperation and EXC_BAD_ACCESS - iphone

I have a few apps which are largely data driven, so most screens are basically composed of:
Open the screen
Download the data via an NSOperation
Display data in a UITableView
Make a selection from the UITableView
Go to new screen, and start over from step 1
I am finding that everything works in normal usage, but if the user goes away from the app for a while and then comes back, I'm getting an EXC_BAD_ACCESS error when the next NSOperation runs. This doesn't seem to matter if the user sends the app into the background or not, and it only seems to occur if there's been at least a few mins since the previous data connection was made.
I realise this must be some form of over-releasing, but I'm pretty good with my memory management and I can't see anything wrong. My data calls generally look like this:
-(void)viewDidLoad {
[super viewDidLoad];
NSOperationQueue* tmpQueue = [[NSOperationQueue alloc] init];
self.queue = tmpQueue;
[tmpQueue release];
}
-(void)loadHistory {
GetHistoryOperation* operation = [[GetHistoryOperation alloc] init];
[operation addObserver:self forKeyPath:#"isFinished" options:0 context:NULL];
[self.queue addOperation:operation];
[operation release];
}
-(void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context {
if ([keyPath isEqual:#"isFinished"] && [object isKindOfClass:[GetHistoryOperation class]]) {
GetHistoryOperation* operation = (GetHistoryOperation*)object;
if(operation.success) {
[self performSelectorOnMainThread:#selector(loadHistorySuceeded:) withObject:operation waitUntilDone:YES];
} else {
[self performSelectorOnMainThread:#selector(loadHistoryFailed:) withObject:operation waitUntilDone:YES];
}
} else {
[super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
}
}
-(void)loadHistorySuceeded:(GetHistoryOperation*)operation {
if([operation.historyItems count] > 0) {
//display data here
} else {
//display no data alert
}
}
-(void)loadHistoryFailed:(GetHistoryOperation*)operation {
//show failure alert
}
And my operations generally looks something like this:
-(void)main {
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSError* error = nil;
NSString* postData = [self postData];
NSDictionary *dictionary = [RequestHelper performPostRequest:kGetUserWalkHistoryUrl:postData:&error];
if(dictionary) {
NSNumber* isValid = [dictionary objectForKey:#"IsValid"];
if([isValid boolValue]) {
NSMutableArray* tmpDays = [[NSMutableArray alloc] init];
NSMutableDictionary* tmpWalksDictionary = [[NSMutableDictionary alloc] init];
NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:#"yyyyMMdd"];
NSArray* walksArray = [dictionary objectForKey:#"WalkHistories"];
for(NSDictionary* walkDictionary in walksArray) {
Walk* walk = [[Walk alloc] init];
walk.name = [walkDictionary objectForKey:#"WalkName"];
NSNumber* seconds = [walkDictionary objectForKey:#"TimeTaken"];
walk.seconds = [seconds longLongValue];
NSString* dateStart = [walkDictionary objectForKey:#"DateStart"];
NSString* dateEnd = [walkDictionary objectForKey:#"DateEnd"];
walk.startDate = [JSONHelper convertJSONDate:dateStart];
walk.endDate = [JSONHelper convertJSONDate:dateEnd];
NSString* dayKey = [dateFormatter stringFromDate:walk.startDate];
NSMutableArray* dayWalks = [tmpWalksDictionary objectForKey:dayKey];
if(!dayWalks) {
[tmpDays addObject:dayKey];
NSMutableArray* dayArray = [[NSMutableArray alloc] init];
[tmpWalksDictionary setObject:dayArray forKey:dayKey];
[dayArray release];
dayWalks = [tmpWalksDictionary objectForKey:dayKey];
}
[dayWalks addObject:walk];
[walk release];
}
for(NSString* dayKey in tmpDays) {
NSMutableArray* dayArray = [tmpWalksDictionary objectForKey:dayKey];
NSSortDescriptor *sortDescriptor = [[NSSortDescriptor alloc] initWithKey:#"startDate" ascending:YES];
NSArray *sortDescriptors = [NSArray arrayWithObject:sortDescriptor];
NSArray* sortedDayArray = [dayArray sortedArrayUsingDescriptors:sortDescriptors];
[sortDescriptor release];
[tmpWalksDictionary setObject:sortedDayArray forKey:dayKey];
}
NSSortDescriptor* sortDescriptor = [NSSortDescriptor sortDescriptorWithKey:nil ascending:NO selector:#selector(localizedCompare:)];
self.days = [tmpDays sortedArrayUsingDescriptors:[NSArray arrayWithObject:sortDescriptor]];
self.walks = [NSDictionary dictionaryWithDictionary:tmpWalksDictionary];
[tmpDays release];
[tmpWalksDictionary release];
[dateFormatter release];
self.success = YES;
} else {
self.success = NO;
self.errorString = [dictionary objectForKey:#"Error"];
}
if([dictionary objectForKey:#"Key"]) {
self.key = [dictionary objectForKey:#"Key"];
}
} else {
self.errorString = [error localizedDescription];
if(!self.errorString) {
self.errorString = #"Unknown Error";
}
self.success = NO;
}
[pool release];
}
-(NSString*)postData {
NSMutableString* postData = [[[NSMutableString alloc] init] autorelease];
[postData appendFormat:#"%#=%#", #"LoginKey", self.key];
return [NSString stringWithString:postData];
}
----
#implementation RequestHelper
+(NSDictionary*)performPostRequest:(NSString*)urlString:(NSString*)postData:(NSError**)error {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
NSURL* url = [NSURL URLWithString:[NSString stringWithFormat:#"%#/%#", kHostName, urlString]];
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:url cachePolicy:NSURLRequestReloadIgnoringCacheData timeoutInterval:30];
[urlRequest setHTTPMethod:#"POST"];
if(postData && ![postData isEqualToString:#""]) {
NSString *postLength = [NSString stringWithFormat:#"%d", [postData length]];
[urlRequest setHTTPBody:[postData dataUsingEncoding:NSASCIIStringEncoding]];
[urlRequest setValue:postLength forHTTPHeaderField:#"Content-Length"];
[urlRequest setValue:#"application/x-www-form-urlencoded" forHTTPHeaderField:#"Content-Type"];
}
NSURLResponse *response = nil;
error = nil;
NSData *jsonData = [NSURLConnection sendSynchronousRequest:(NSURLRequest *)urlRequest returningResponse:(NSURLResponse **)&response error:(NSError **)&error];
NSString *jsonString = [[NSString alloc] initWithBytes: [jsonData bytes] length:[jsonData length] encoding:NSUTF8StringEncoding];
NSLog(#"JSON: %#",jsonString);
//parse JSON
NSDictionary *dictionary = nil;
if([jsonData length] > 0) {
dictionary = [[CJSONDeserializer deserializer] deserializeAsDictionary:jsonData error:error];
}
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
return dictionary;
}
If I have the autorelease pool in place, the crash occurs on [pool release]. If I don't, then the crash just looks to appear in the main.m method, and I don't seem to get any useful information. It's difficult to track down when I have to wait 10 mins in between every test!
If anyone can offer any clues or directions to go, that'd be much appreciated.

It's almost certain you're overreleasing something in your code, seeing that the crash is occurring during a [pool release] (There's a autorelease pool in the main method as well).
You can find it using Xcode - use build and analyze to have the static analyser pinpoint potential problems. Run it and post the results.

try this:
http://cocoadev.com/index.pl?NSZombieEnabled
also, you should avoid:
1) calling UIKit methods from secondary threads
2) making (synchronous) url requests from the main thread.
you must be doing one in any case in RequestHelper's performPostRequest method.

My guess is this section
GetHistoryOperation* operation = (GetHistoryOperation*)object;
if(operation.success) {
[self performSelectorOnMainThread:#selector(loadHistorySuceeded:) withObject:operation waitUntilDone:YES];
} else {
[self performSelectorOnMainThread:#selector(loadHistoryFailed:) withObject:operation waitUntilDone:YES];
}
If the sleep happens at a bad point here, you have an object being passed to another thread. I'd find a way around having to pass the operation as the object.

This is a really old question, so sorry for the dredge, but there is no accepted answer.
I was also getting a EXC_BAD_ACCESS on NSOperationQueue -addOperation for seemingly no reason, and after a few days of hunting down memory leaks, and turning on all the debugger options i could find (malloc guard, zombies) and getting nothing, I found an NSLog warning that said: "[NSoperation subclass] set to IsFinished before being started by the queue."
When I modified my base operation subclass, so that its -cancel function only set (IsRunning = NO) and (IsFinished = YES) IF AND ONLY IF (IsRunning == YES), NSOperationQueue stopped crashing.
So if you're ever calling NSOperationQueue -cancelAllOperations, or you're doing that manually (i.e. for (NSOperation *op in queue.allOperations) ) double check to make sure that you don't set IsFinished on those operations in your subclass implementation.

Related

Memory Leaks while using MFMailComposer

I am trying to send a CSV file through MfMail Composer.Everything works fine but there are lot leaks while using Instruments.I am not able trace out where I went wrong.Here is my code.
-(NSData *)getCSV
{
NSManagedObjectContext *moc = [(ETAppDelegate *)[[UIApplication sharedApplication] delegate]managedObjectContext];
NSFetchRequest *request = [[[NSFetchRequest alloc]init]autorelease];
[request setEntity:[NSEntityDescription entityForName:#"Expense" inManagedObjectContext:moc]];
NSError *error = nil;
NSArray *results = [moc executeFetchRequest:request error:&error];
NSMutableArray *expensesList = [[[NSMutableArray alloc]init]autorelease];
for(int i = 0;i<[results count];i++){
NSString *category = [[results objectAtIndex:i]valueForKey:#"category"];
NSString *date = [[NSDateFormatter dateFormatterwithMediumStyle]stringFromDate:[[results objectAtIndex:i]valueForKey:#"date"]];
NSString *amount = [NSNumberFormatter localizedStringFromNumber:[[results objectAtIndex:i]valueForKey:#"amount"] numberStyle:NSNumberFormatterCurrencyStyle];
NSString *mailString = [NSString stringWithFormat:#"%#,%#,%#",category,date,amount ];
[expensesList addObject:mailString];
}
NSString *expensesString = [expensesList componentsJoinedByString:#"\n"];
NSData *expensesData = [expensesString dataUsingEncoding:NSUTF8StringEncoding];
return expensesData;
}
-(void)displayComposerSheet
{
NSData *csvFile = [self getCSV];
NSString *csvFileName = #"MyExpenses";
MFMailComposeViewController *mailController = [[MFMailComposeViewController alloc]init];
[mailController setSubject:#"List Of Expenses"];
[mailController setMessageBody:#"Expenses" isHTML:NO];
[mailController addAttachmentData:csvFile mimeType:#"text/csv" fileName:csvFileName];
[mailController setMailComposeDelegate:self];
[self presentModalViewController:mailController animated:YES];
[mailController release];
}
I can't say that I see anything in the code you've provided that should cause you to leak. Your leaks are likely taking place elsewhere.
What I do see, however, is the potential to create a lot of autoreleased objects in that loop in -getCSV, and depending on how many iterations you're performing, that could be almost as bad. The way you've written it, I see a date formatter and three strings headed for your main autorelease pool with every iteration. One suggestion is to create a date formatter outside your loop that you can reuse inside it. Another suggestion is to frame the guts of your loop with a local autorelease pool; that will prevent your main autorelease pool from becoming too large.
Apropos of nothing, you should also consider using fast enumeration.
Here's your loop with the suggestions applied:
NSDateFormatter *myDateFormatter = [NSDateFormatter dateFormatterWithMediumStyle];
NSMutableArray *expensesList = [[[NSMutableArray alloc] init] autorelease];
for (id obj in results)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSString *category = [obj valueForKey:#"category"];
NSString *date = [myDateFormatter stringFromDate:[obj valueForKey:#"date"]];
NSString *amount = [NSNumberFormatter localizedStringFromNumber:[obj valueForKey:#"amount"] numberStyle:NSNumberFormatterCurrencyStyle];
NSString *mailString = [NSString stringWithFormat:#"%#,%#,%#", category, date, amount];
[expensesList addObject:mailString];
[pool release];
}

How to get cookies and use them for other requests like POST ( iOS )?

My previous question was about the problem that I have to login each time for doing web services like posting a link or uploading a picture. Philipe answered that I have to use cookies instead of login process for each request. I found this method for getting cookies:
- (void)getCookies {
NSHTTPURLResponse * response;
NSError * error;
NSMutableURLRequest *request;
request = [[NSMutableURLRequest alloc] initWithURL:[NSURL URLWithString:#"http://MyWebsite.com/login.php"]
cachePolicy:NSURLRequestReloadIgnoringCacheData
timeoutInterval:120];
NSData * data = [NSURLConnection sendSynchronousRequest:request returningResponse:&response error:&error];
NSLog(#"%#", [[NSString alloc] initWithData:data encoding:NSASCIIStringEncoding]);
NSArray * all = [NSHTTPCookie cookiesWithResponseHeaderFields:[response allHeaderFields] forURL:[NSURL URLWithString:#"http://MyWebsite.com/login.php"]];
NSLog(#"%d", all.count);
for (NSHTTPCookie *cookie in all) {
NSLog(#"Name: %# : Value: %#", cookie.name, cookie.value);
NSLog(#"Comment: %# : CommentURL: %#", cookie.comment, cookie.commentURL);
NSLog(#"Domain: %# : ExpiresDate: %#", cookie.domain, cookie.expiresDate);
NSLog(#"isHTTPOnly: %c : isSecure: %c", cookie.isHTTPOnly, cookie.isSecure);
NSLog(#"isSessionOnly: %c : path: %#", cookie.isSessionOnly, cookie.path);
NSLog(#"portList: %# : properties: %#", cookie.portList, cookie.properties);
NSLog(#"version: %u", cookie.version);
}
}
I also found this code to use these cookies, but I'm not sure how to use it:
[[NSHTTPCookieStorage sharedHTTPCookieStorage] setCookie:cookies];
Here is my method for POSTing, I am using RestKit API:
- (IBAction)addLinkPressed:(UIButton *)sender {
[RKClient clientWithBaseURLString:#"http://MyWebsite.com"];
NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys:
self.linkField.text, #"url",
self.linkTitleField.text, #"title",
self.linkSummaryField.text, #"summary",
nil];
RKRequest *request = [[RKClient sharedClient] post:#"/send_link.php" params:params delegate:self];
[request setUserData:#"sendLink"];
}
Question: Which property of cookies should I store to use it for login information and where should I put it in my code?
I solved this issue by some inefficient way. Here is my methodology:
First I try to post to the web service and after posting I parse the returning HTML to see if the posting was successful or not. If posting was successful I give an appropriate message to the user that you post successfully but if it was not successful it could have two reasons: First: there were some error during the post execution Second: the user was not logged in. The way that I recognize the differentiation between fist and second error is just parsing the response HTML.
Here is the code that I used for this methodology (this is for the time that the user wants to change the password)
- (void)objectLoader:(RKObjectLoader*)objectLoader didFailWithError:(NSError*)error {
NSRange range = [[error localizedDescription] rangeOfString:#"-1012"];
if (range.length > 0){
//First error occurs here
}
RKLogError(#"Hit error: %#", error);
}
- (IBAction)requestToChangePasswordPressed {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Loading";
[RKClient clientWithBaseURLString:#"http://WebServiceDomain.com"];
NSDictionary* params = [NSDictionary dictionaryWithObjectsAndKeys:
self.oldPasswordField.text, #"oldPassword",
self.passwordNew.text, #"newPassword",
self.confirmPasswordField.text, #"confirmPassword",
nil];
RKRequest *request = [[RKClient sharedClient] post:#"/change_password.php" params:params delegate:self];
[request setUserData:#"changePassword"];
[self.view endEditing:YES];
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
- (void)autoLogin {
[RKClient clientWithBaseURLString:#"http://WebServiceDomain.com"];
[RKObjectManager sharedManager].client=[RKClient sharedClient];
RKParams *parameters = [RKParams params];
[parameters setValue:[[NSUserDefaults standardUserDefaults] objectForKey:#"defaultUsername"] forParam:#"username"];
[parameters setValue:[[NSUserDefaults standardUserDefaults] objectForKey:#"defaultPassword"] forParam:#"password"];
[[RKClient sharedClient] setAuthenticationType:RKRequestAuthenticationTypeHTTP];
// because we have two POSTs and we want to use the same method for both of the for didLoadResponse: we set the UserDate like bellow
RKRequest *request = [[RKClient sharedClient] post:#"/login.php" params:parameters delegate:self];
[request setUserData:#"login"];
}
- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response
{
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Loading";
id userData = [request userData];
if ([userData isEqual:#"login"]) {
if ([request isGET]) {
// Handling GET /foo.xml
if ([response isOK]) {
// Success! Let's take a look at the data
NSLog(#"Retrieved XML: %#", [response bodyAsString]);
}
} else if ([request isPOST]) {
// Handling POST /other.json
if ([response isJSON]) {
NSLog(#"Got a JSON response back from our POST!");
}
} else if ([request isDELETE]) {
// Handling DELETE /missing_resource.txt
if ([response isNotFound]) {
NSLog(#"The resource path '%#' was not found.", [request resourcePath]);
}
}
}
else if ([userData isEqual:#"sendLink"]) {
NSData *addLinksHtmlData = response.body;
// 2
TFHpple *addlinksParser = [TFHpple hppleWithHTMLData:addLinksHtmlData];
// 3
NSString *errorLinksXpathQueryString = #"//div[#class='errorBox']/ul/li";
NSArray *errorLinksNodes = [addlinksParser searchWithXPathQuery:errorLinksXpathQueryString];
// 4
NSMutableArray *newErrorLinks = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in errorLinksNodes) {
// 5
AllModels *errorTitle = [[AllModels alloc] init];
[newErrorLinks addObject:errorTitle];
// 6
errorTitle.errorTitle = [[element firstChild] content];
}
// 8
self.linkErrorObjects = newErrorLinks;
NSString *successLinksXpathQueryString = #"//div[#class='successBox']";
NSArray *successLinksNodes = [addlinksParser searchWithXPathQuery:successLinksXpathQueryString];
// 4
NSMutableArray *newSuccessLinks = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in successLinksNodes) {
// 5
AllModels *successTitle = [[AllModels alloc] init];
[newSuccessLinks addObject:successTitle];
// 6
successTitle.successTitle = [[element firstChild] content];
}
// 8
self.linkSuccessObjects = newSuccessLinks;
}
else {
NSLog(#"HTTP status code: %d", response.statusCode);
NSLog(#"HTTP status message: %#", [response localizedStatusCodeString]);
NSLog(#"Header fields: %#", response.allHeaderFields);
NSLog(#"Body: %#", response.bodyAsString);
NSData *HtmlData = response.body;
// 2
TFHpple *addParser = [TFHpple hppleWithHTMLData:HtmlData];
// 3
NSString *errorXpathQueryString = #"//div[#class='errorBox']/ul/li";
NSArray *errorNodes = [addParser searchWithXPathQuery:errorXpathQueryString];
// 4
NSMutableArray *newError = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in errorNodes) {
// 5
AllModels *errorTitle = [[AllModels alloc] init];
[newError addObject:errorTitle];
// 6
errorTitle.errorTitle = [[element firstChild] content];
}
// 8
self.ErrorObjects = newError;
NSString *successXpathQueryString = #"//div[#class='successBox']";
NSArray *successNodes = [addParser searchWithXPathQuery:successXpathQueryString];
// 4
NSMutableArray *newSuccess = [[NSMutableArray alloc] initWithCapacity:0];
for (TFHppleElement *element in successNodes) {
// 5
AllModels *successTitle = [[AllModels alloc] init];
[newSuccess addObject:successTitle];
// 6
successTitle.successTitle = [[element firstChild] content];
}
// 8
self.successObjects = newSuccess;
[self errorCheck];
}
[MBProgressHUD hideHUDForView:self.view animated:YES];
[MBProgressHUD hideHUDForView:self.view animated:YES];
}
- (void)errorCheck {
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.labelText = #"Loading";
if(self.errorObjects.count > 0) {
AllModels *errorlink = [self.errorObjects objectAtIndex:0];
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"There is a problem" message:errorlink.errorTitle delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles:nil , nil];
[alert show];
}
else {
if(self.linkErrorObjects.count > 0) {
[self autoLogin];
[self requestToChangePasswordPressed];
}
else {
AllModels *successlink = [self.successObjects objectAtIndex:0];
self.successLabel.hidden = NO;
self.successLabel.text = successlink.successTitle;
NSLog(#"Success Title: %#",successlink.successTitle);
[UIView animateWithDuration:3.0
delay:0.0
options:UIViewAnimationOptionBeginFromCurrentState
animations:^{ self.successLabel.alpha = 0.0; }
completion:^(BOOL fin) { if (fin) [self.successLabel removeFromSuperview]; }];
[self performSelector:#selector(dismissModalViewController) withObject:nil afterDelay:1.0];
}
}
[MBProgressHUD hideHUDForView:self.view animated:YES];
[MBProgressHUD hideHUDForView:self.view animated:YES];
}

Core Data Import Failure

I'm trying to import a large data set (~6,000) in to my core data application. I've read the Apple document "Efficiently Importing Data" and I think I set it up correctly. The weird thing is the application isn't crashing in the simulator, although it does if I run it with the Leaks instrument, but it isn't saving all the data. Sometimes it will only save 3-4 hundred other times it will save 3-4 thousand and rarely the whole data set. I think it's probably memory leak related and I'm pretty new to using NSAutoReleasePool, any help would be much appreciated.
NSURL *url = [NSURL URLWithString:#""];
NSString *responseString = [NSString stringWithContentsOfURL:url encoding:NSASCIIStringEncoding error:nil];
if (responseString) {
NSArray *players = [responseString componentsSeparatedByString:#";"];
NSUInteger LOOP_LIMIT = 100, count = 0;
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
NSManagedObjectContext *context = [[AppController sharedAppController] managedObjectContext];
[context setUndoManager:nil];
for (int i=0; i<([players count] - 1); i++) {
NSArray *info = [[players objectAtIndex:i] componentsSeparatedByString:#","];
NSString *dateInfo = [info objectAtIndex:10];
NSLocale *usLocale = [[[NSLocale alloc] initWithLocaleIdentifier:#"en_US"] autorelease];
NSDateFormatter *fo = [[[NSDateFormatter alloc] init] autorelease];
[fo setDateFormat:#"MM/dd/yyyy"];
[fo setLocale:usLocale];
[fo setTimeZone:[NSTimeZone systemTimeZone]];
NSDate *dob = [fo dateFromString:dateInfo];
Players *player = [NSEntityDescription insertNewObjectForEntityForName:#"Players"
inManagedObjectContext:context];
NSNumberFormatter *f = [[[NSNumberFormatter alloc] init] autorelease];
[f setNumberStyle:NSNumberFormatterNoStyle];
player.playerID = [f numberFromString:[info objectAtIndex:0]];
player.lastName = [info objectAtIndex:1];
player.firstName = [info objectAtIndex:2];
player.position = [info objectAtIndex:4];
NSString *teamName = [info objectAtIndex:3];
NSFetchRequest *req = [[[NSFetchRequest alloc] init] autorelease];
NSEntityDescription *ent = [NSEntityDescription entityForName:#"Teams" inManagedObjectContext:context];
[req setEntity:ent];
[req setIncludesPropertyValues:NO];
NSPredicate *pre = [NSPredicate predicateWithFormat:#"team=%#", teamName];
[req setPredicate:pre];
NSArray *list = [context executeFetchRequest:req error:nil];
if ([list count]) {
Teams *team = [list objectAtIndex:0];
player.team_Players_Teams = team;
}
count++;
if (count == LOOP_LIMIT) {
[context save:nil];
[context reset];
[pool drain];
pool = [[NSAutoreleasePool alloc] init];
count = 0;
}
}
if (count != 0) {
NSLog(#"In Save Remaining");
[context save:nil];
[context reset];[pool drain];
}
I can't see anything dodgy in the code either. Definitely no errors appearing in the log?
Btw one other optimisation tip covered in the Core Data pdf for importing data is to move the predicate creation outside the loop and use substitution variables.
I can't see any obvious leaks but:
You could minimise the amount of memory used by alloc init retain-ing the NSLocale, NSDateFormatter, NSNumberFormatter and then release-ing them after the loop is complete. These don't seem to change between runs of the loop.
I don't know Core Data but where does the NSManagedObject/Player *player object get released? Is this autoreleased through Core Data?
As an aside, you can use [list lastObject] rather than [list count] and [list objectAtIndex:0] as the last two will crash if list is nil
Update based on response:
If nothing appears to make a difference then the next step is to simplify the code so as to remove any error sources.
Carry out my suggestions in #1 above to minimise the amount of code in the loop.
Check you're releasing the list object somewhere or that it is allocated as autorelease.
Remove the intermediate saving (all that code inside count == LOOP_LIMIT) and only save and drain the pool once all the arrays have been processed. You also should not need the code following on inside the if (count != 0)
Replace the error:nil statements with proper error:&&error and log errors. To log the errors do the following (apologies but the code formatting doesn't seem to work - no idea why):
NSError *error = nil; //Declared upfront
// Your streamlined code
// ....
error = nil; //Just before a fetchRequest or context:save
/* After looping through all your code now attempt to save */
if(![context save:&error]) {
NSLog(#"Failed to save to data store: %#", [error localizedDescription]);
NSArray *detailedErrors = [[error userInfo] objectForKey:NSDetailedErrorsKey];
if(detailedErrors != nil && [detailedErrors count] > 0) {
for(NSError *detailedError in detailedErrors) {
NSLog(#"DetailedError: %#", [detailedError userInfo]);
}
} else {
NSLog(#" %#", [error userInfo]);
}
}
Then check the log to see if you're getting any odd messages. You can also use similar code anywhere in Core Data that you need to check for errors (i.e. executeFetchRequest). It's worth a shot to find out what the error is here.

Memory Leak related

i am working on a fishing app these days and i am getting a memory leak problem
-(void)requestFinished:(ASIFormDataRequest *) request {
if(hud != nil){
[hud show:NO];
[hud release];
hud = nil;
}
isLoading = NO;
self.responseText = [request responseString];
[self parseXml]; //I am getting leak here
if ( [self.responseText hasPrefix:#"<result>"]) {
UIAlertView *info = [[[UIAlertView alloc] initWithTitle:#" " message:#"Limited Internet access, please find a stronger signal in the area" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil]autorelease];
[info show];
}
if (!isRefreshButtonClicked) {
[UIAccelerometer sharedAccelerometer].delegate = self;
[NSThread detachNewThreadSelector:#selector(parseXml) toTarget:self withObject:nil];
} }
This is my function...
-(void) parseXml
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
_fishes = [[fishes parseXml:self.responseText] retain];
[self performSelectorOnMainThread:#selector(parseXmlDone) withObject:nil waitUntilDone:YES];
[pool release];
Here _fishes is an array which is getting a value from a array return type function.....and here is that function...
+(NSMutableArray *)parseXml:(NSString *)xmlString {
//xmlString = [xmlString stringByReplacingOccurrencesOfString:#"&" withString:#""];
const char *cString = [xmlString UTF8String];
NSMutableArray *fishes = [NSMutableArray array];
NSData *xmlData = [NSData dataWithBytes:cString length:strlen(cString)];
NSError *error;
GDataXMLDocument *doc = [[GDataXMLDocument alloc]initWithData:xmlData options:0 error:&error];
if (doc == nil) { return nil; }
//parseXml
NSArray *_fishes = [doc.rootElement elementsForName:#"fishery"];
for (GDataXMLElement *_fish in _fishes) {
NSMutableDictionary *fish = [NSMutableDictionary dictionary];
NSArray *ids = [_fish elementsForName:#"id"];
if ([ids count]>0) {
GDataXMLElement *firstId = (GDataXMLElement *)[ids objectAtIndex:0];
[fish setValue:firstId.stringValue forKey:#"id"];
} else continue;
NSArray *names = [_fish elementsForName:#"name"];
if ([names count]>0) {
GDataXMLElement *firstName = (GDataXMLElement *)[names objectAtIndex:0];
[fish setValue:firstName.stringValue forKey:#"name"];...........
........
else continue;
NSArray *distances = [_fish elementsForName:#"distance"];
if ([distances count]>0) {
GDataXMLElement *distance = (GDataXMLElement *)[distances objectAtIndex:0];
[fish setValue:distance.stringValue forKey:#"distance"];
}else continue;
[fishes addObject:fish];
}
[doc release];
return fishes;
}
#end
I hope u guys will understand my problem...thanx
In -parseXml,
_fishes = [[fishes parseXml:self.responseText] retain];
will leak any previous object _fishes was pointing to in case -parseXml is sent more than once. You could use a retain property instead of an instance variable, or a setter method that releases the previous object, or release the previous object before assigning a new (retained) object to _fishes.

Problem passing NSError back as a return parameter

I am having a problem passing an NSError object back. The first line of code to access the object (in this case, I inserted an NSLog) causes "EXC_BAD_ACCESS".
Is this because I am not explicitly creating an NSError object, but rather getting one from the NSURLRequest and passing it back? In this particular function (downloadFile:), some errors I want to retrieve from other functions, but I create an NSError on two other occasions in the function.
Any help is appreciated.
Here is the offending code:
-(void)someCode {
NSError *err = nil;
localPool = [[NSAutoreleasePool alloc] init];
if (!iap) {
iap = [[InAppPurchaseController alloc] init];
}
if (![self.iap downloadFile:#"XXXXX.plist" withRemoteDirectory:nil withLocalDelete:YES withContentType:#"text/xml" Error:&err] ) {
//"EXC_BAD_ACCESS" on calling NSLog on the next line?
NSLog(#"Error downloading Plist: %#", [err localizedDescription]);
[self performSelectorOnMainThread:#selector(fetchPlistFailed:) withObject:err waitUntilDone:NO];
[localPool drain], localPool = nil;
return NO;
}
//Removed the remainder of the code for clarity.
[localPool drain], localPool = nil;
return YES;
}
-(BOOL)downloadFile:(NSString *)fileName
withRemoteDirectory:(NSString *)remoteDirectory
withLocalDelete:(BOOL)withLocalDelete
withContentType:(NSString *)contentTypeCheckString
Error:(NSError **)error {
UIApplication *app = [UIApplication sharedApplication];
app.networkActivityIndicatorVisible = YES;
NSError *localError = nil;
NSAutoreleasePool *localPool = [[NSAutoreleasePool alloc] init];
NSString *urlString = [NSString stringWithFormat:#"http://XXXXX/%#", fileName];
NSLog(#"Downloading file: %#", urlString);
NSURL *url = [NSURL URLWithString:urlString];
NSURLRequest *req = [[NSURLRequest alloc] initWithURL:url];
NSHTTPURLResponse *response = nil;
NSData *responseData = [NSURLConnection sendSynchronousRequest:req returningResponse:&response error:&localError];
[req release];
if (response == nil || localError) {
NSLog(#"Error retrieving file:%#", [localError localizedDescription]);
if (error != NULL) {
*error = localError;
//THIS NSLog call works just fine.
NSLog(#"Error copied is:%#", [*error localizedDescription]);
}
[localPool drain], localPool = nil;
app.networkActivityIndicatorVisible = NO;
return NO;
}
//Rest of function omitted for simplicity.
}
I guess your NSError object is autoreleased and put on your localPool. You drained that localPool, thus destroying the NSError.
Do you really need localPool in every method? If not, just remove the localPools.
Also, it looks like you forgot to drain the localPool in someCode. Hopefully you just didn't copy it...
-(void)someCode {
NSError *err = nil;
localPool = [[NSAutoreleasePool alloc] init];
if (!iap) {
iap = [[InAppPurchaseController alloc] init];
}
if (![self.iap downloadFile:#"XXXXX.plist" withRemoteDirectory:nil withLocalDelete:YES withContentType:#"text/xml" Error:&err] ) {
....
[localPool drain], localPool = nil;
return NO;
}
[localPool drain], localPool = nil; // missing
}