I have an application that works fine in an older project (which does not use ARC and was written against in Xcode 4.0 with iOS 4.2.
I am trying to port this application to using ARC and storyboards with Xcode 4.2 and iOS 5.
I have everything ported and fixed all errors caused by saving, releasing, canceling the release, etc. Now the project is building perfectly. I also rebuilt the Core Data model in a new project and rebuilt the subclasses of the model.
If an error occurs when I try to parse an XML file in a background thread. The file is local to the project. I am an NSLog initializer for each NSOperation class that performs parsing (there are four of them). I get to this point in the application, an error occurs when I add operations to the queue.
Here is my code in AppDelegate that runs the Operation:
#import "AppDelegate.h" #import "ACHPhonebook.h" @implementation AppDelegate @synthesize window = _window; @synthesize managedObjectContext = __managedObjectContext; @synthesize managedObjectModel = __managedObjectModel; @synthesize persistentStoreCoordinator = __persistentStoreCoordinator; @synthesize parseQueue; @synthesize parseOpAD, parseOpEK, parseOpLR, parseOpSZ; - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { NSFetchRequest *request = [[NSFetchRequest alloc] init]; NSEntityDescription *entity = [NSEntityDescription entityForName:@"ACHPhonebook" inManagedObjectContext:self.managedObjectContext]; [request setEntity:entity]; NSError *error = nil; NSUInteger count = [self.managedObjectContext countForFetchRequest:request error:&error]; NSLog(@"total CD count = %d", count); if( getenv("NSZombieEnabled") || getenv("NSAutoreleaseFreedObjectCheckEnabled") ) { NSLog(@"NSZombieEnabled/NSAutoreleaseFreedObjectCheckEnabled enabled!"); } if (count == 0) {
Here is one of the NSOperation classes (they are all the same, they just work with different files to speed up the initial loading of 6000 records.
#import "ParseOperationA-Dh" #import "ACHPhonebook.h" #import "AppDelegate.h" #import "TBXML.h" @implementation ParseOperationA_D - (id)initAndStartParse { NSLog(@"ParseOperation init"); // [self parsePhonebook]; return self; } - (void)mergeChanges:(NSNotification *)notification { id appDelegate = [[UIApplication sharedApplication] delegate]; NSManagedObjectContext *mainContext = [appDelegate managedObjectContext]; // Merge changes into the main context on the main thread [mainContext performSelectorOnMainThread:@selector(mergeChangesFromContextDidSaveNotification:) withObject:notification waitUntilDone:NO]; // NSLog(@"Merged Changes"); } // the main function for this NSOperation, to start the parsing - (void)main { id appDelegate = [[UIApplication sharedApplication] delegate]; NSManagedObjectContext *ctx = [[NSManagedObjectContext alloc] init]; [ctx setUndoManager:nil]; [ctx setPersistentStoreCoordinator: [appDelegate persistentStoreCoordinator]]; // Register context with the notification center NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; [nc addObserver:self selector:@selector(mergeChanges:) name:NSManagedObjectContextDidSaveNotification object:ctx]; NSUInteger x = 1; TBXML *tbxml = [[TBXML alloc] initWithXMLFile:@"achcentral_aTOd.xml"]; // Get root element TBXMLElement * root = tbxml.rootXMLElement; // if root element is valid if (root) { NSError *error = nil; TBXMLElement *thisPBE = [TBXML childElementNamed:@"PhoneBookResults" parentElement:root]; while (thisPBE != nil) { // Set up the Insert command ACHPhonebook * xmlPBE = (ACHPhonebook *)[NSEntityDescription insertNewObjectForEntityForName:@"ACHPhonebook" inManagedObjectContext:ctx]; // Set all the field values from the XML record // TBXMLElement *elName = [TBXML childElementNamed:@"Name" parentElement:thisPBE]; // if (elName != nil) { // [xmlPBE setName:[TBXML textForElement:elName]]; // } TBXMLElement *elTitle = [TBXML childElementNamed:@"title" parentElement:thisPBE]; if (elTitle != nil) { [xmlPBE setTitle:[TBXML textForElement:elTitle]]; } TBXMLElement *elDept = [TBXML childElementNamed:@"department" parentElement:thisPBE]; if (elDept != nil) { // obtain the text from the description element [xmlPBE setDepartment:[TBXML textForElement:elDept]]; } TBXMLElement *elExt = [TBXML childElementNamed:@"phone" parentElement:thisPBE]; if (elExt != nil) { [xmlPBE setExt:[TBXML textForElement:elExt]]; } TBXMLElement *elPager = [TBXML childElementNamed:@"pager" parentElement:thisPBE]; if (elPager != nil) { [xmlPBE setPager:[TBXML textForElement:elPager]]; } TBXMLElement *elEmailAddress = [TBXML childElementNamed:@"emailaddress" parentElement:thisPBE]; if (elEmailAddress != nil) { [xmlPBE setEmailAddress:[TBXML textForElement:elEmailAddress]]; } // TBXMLElement *elNetworkID = [TBXML childElementNamed:@"NetworkID" parentElement:thisPBE]; // if (elNetworkID != nil) { // [xmlPBE setNetworkid:[TBXML textForElement:elNetworkID]]; // } TBXMLElement *elLastName = [TBXML childElementNamed:@"lastName" parentElement:thisPBE]; if (elLastName != nil) { [xmlPBE setLastName:[TBXML textForElement:elLastName]]; } TBXMLElement *elFirstName = [TBXML childElementNamed:@"firstName" parentElement:thisPBE]; if (elFirstName != nil) { [xmlPBE setFirstName:[TBXML textForElement:elFirstName]]; } if (elFirstName != nil && elLastName !=nil) { // Make the name field from the first and last names NSString *fullName = [[TBXML textForElement:elFirstName] stringByAppendingFormat:@" %@",[TBXML textForElement:elLastName]]; [xmlPBE setName:fullName]; } TBXMLElement *elPicLoc = [TBXML childElementNamed:@"picFileName" parentElement:thisPBE]; if (elPicLoc != nil) { if (![[TBXML textForElement:elPicLoc] isEqualToString:@""]) { [xmlPBE setPicloc:[TBXML textForElement:elPicLoc]]; NSString *picFilePath = @"https://secure.archildrens.org/insite/badgepics/adbadgepics/"; NSURL *url = [NSURL URLWithString:[picFilePath stringByAppendingString:xmlPBE.picloc]]; NSData * imageData = [[NSData alloc] initWithContentsOfURL: url]; if (!imageData) { // The image filename was stored but didn't exist NSString *achImage = [[NSBundle mainBundle] pathForResource:@"ach" ofType:@"png"]; imageData = [NSData dataWithContentsOfFile:achImage]; } [xmlPBE setPicture:imageData]; } } if (x % 50 == 0) { // Get ready to save the context error = nil; // Save the context. if (![ctx save:&error]) { NSLog(@"loadPhoneBook error %@, %@", error, [error userInfo]); abort(); } // Clear out the scratchpad [ctx reset]; //NSLog(@"Got 10"); } // Find the next XML record (this will end the while loop when we reach the end) thisPBE = [TBXML nextSiblingNamed:@"PhoneBookResults" searchFromElement:thisPBE]; // Increment the counter x++; } // while ... if ([ctx hasChanges]) { // NSUInteger *left = [[ctx insertedObjects] count]; error = nil; // Save the context. if (![ctx save:&error]) { NSLog(@"loadPhoneBook error %@, %@", error, [error userInfo]); abort(); } // Clear out the scratchpad [ctx reset]; NSLog(@"Got the last ones, %d", x); } } } @end
I tried to enable NSZombies
and still do not see what is happening. It always aborts with EXC_BAD_ACCESS
error on
[parseQueue addOperations:opArray waitUntilFinished:NO]
at AppDelegate
, this was my first attempt at multithreading in an iOS app. As I said, I have a version that works, which was created against the old version of the SDK, and it still works. It could be (and probably have) something really simple that I'm missing ...