core data exc_bad_access when setting relationships - iphone

I have a problem with core data when setting relationships:
The code below crashes randomly when setting the relationships between country and region.
If I disable the second for-loop, the method completes without errors.
Everything happens within the context living on the background-thread.
Again: I can create the objects for the regions and countries without trouble and they show up in the Simulators database just fine - but as soon as I try to set the relationships between then, the app crashes randomly.
Any thoughts ?
for (Region* region in regions) {
// only store if region code isn't empty
if (region.m_RegionCode != nil && [region.m_RegionCode length] > 0) {
NSManagedObject* cdRegion = [NSEntityDescription insertNewObjectForEntityForName:CDREGION inManagedObjectContext:self.objectContextBackground];
[cdRegion setValue:region.m_RegionCode forKey:#"code"];
[cdRegion setValue:region.m_regioncodedescription forKey:#"name"];
}
}
[self saveBackgroundContext];
for (Region* region in regions) {
if (region.m_RegionCode != nil && [region.m_RegionCode length] > 0) {
NSManagedObject* cdRegion = [self getManagedObject:CDREGION withCode:region.m_RegionCode];
NSManagedObject* CDCountry = [self getManagedObject:CDCOUNTRY withCode:region.m_countrycode];
[cdRegion setValue:CDCountry forKey:#"country"];
}
}

well - just to let you know: it actually was the problem (I know - it is listed on top of all pages regarding this topic :-) that I used a context between threads.
I mixed up queues and threads. I created a single background-queue where I used my "background-context" ... but of course i created several threads within that, who where interacting with the context... so...
btw: it was this excellent article that finally clarified it for me:
(came right in time :-)
http://www.cimgf.com/2011/08/22/importing-and-displaying-large-data-sets-in-core-data/

This error message is likely related to memory problem, about using (or releasing) already deallocated object.
You should run profiler to spot the memory problem, or debug with a breakpoint after the save method and following a line by line execution until it crashes.
Just guessing, if you are creating or getting the Regions object with some sort of factory method, probably it gets deallocated in the middle of execution by that method, sometimes the routine is fast enough to complete before the dealloc, sometimes not, that could explain the randomness.
Try to retain the Regions at the beginning and release at the end of the second loop.

Related

iOS Core Data - Updating Multiple records

I've scoured and still haven't found anything that quite works. Either the question/answer is too old or it simply hasn't worked for me. This is my first attempt at "my own" app. As it seems a right of passage, I'm making a checklist app. Here's what I'm looking for:
My Data Store contains 4 attributes: name, category, isChecked, isActive (more will surely follow as I expand)
When my View Controller initially loads, the NSFetchedResultsController has an NSPredicate that only retrieves the records whose attribute isActive is YES (or [NSNumber numberWithBool:YES). It then takes those records and displays them into the appropriate cells for the user. When a user clicks on a cell, the Data Store updates and changes the isChecked attribute accordingly. Everything works good to this point.
What I need to do now is to be able to remove the items (1 or more) from the list. Specifically, I need it to update the Data Store attributes isChecked and isActive to NO only if it's current isChecked attribute is YES. (I'm not looking to delete the record from the data store as they will be used to build up the database for the users future use.)
I've used, among other things:
[[[self fetchedResultsController] fetchedObjects]
setValue:[NSNumber numberWithBool:NO]
forKey:#"isChecked"];
This does actually work, it removes the checkmark(s) and updates the store accordingly. Problem is, not only am I making another request to the data store for the isActive items, it also searches the entire "Active List" that was fetched and sets each of their isChecked attributes to NO. This may not be too big of an issue for small lists, but as the list(s) expand this can be an issue.
The other problem is, if I add:
[[[self fetchedResultsController] fetchedObjects]
setValue:[NSNumber numberWithBool:NO]
forKey:#"isActive"];
It sets ALL of my list items to NO (as well as a second data store request within the same method.)
So my question is: How can I get through the list, find only the items that are checked and update only those records (set both the isChecked && isActive attributes = NO) whose isChecked attribute is YES rather than working through the entire list?
I've tried creating a separate fetchedResultsController specifically for this buttons action, and it did work (that is to say, it didn't crash) but the debugger popped out a rather large 'Serious Application Error'. I won't post the error message as it's long and most likely irrelevant to any solution.
Any assistance would be greatly appreciated. Thanks in advance and please be gentle :-].
EDIT
I have tried using a for loop, for (NSString *item in fetchedResultsController) but I get the error ...may not respond to 'countByEnumeratingWithState:objects:count'
It seems a loop of sorts is what's needed here, but again, nothing I can find is relevant or it's outdated. Again, thanks for any assistance.
Edit 2
Here is the original error I got when I ran a second separate fetchRequestController for this button/method:
An exception was caught from the delegate of NSFetchedResultsController during a call to -controllerDidChangeContent:. Invalid update: invalid number of rows in section 0. The number of rows contained in an existing section after the update (4) must be equal to the number of rows contained in that section before the update (4), plus or minus the number of rows inserted or deleted from that section (0 inserted, 3 deleted) and plus or minus the number of rows moved into or out of that section (0 moved in, 0 moved out). with userInfo (null)
You can just loop over the fetchedObjects collection and change the managed objects. After changing them you'll need to reload your list (I guess you use a tableview).
I don't know what your classes are named, but in general you can just loop over the collection of managed objects and change them. Remember that you need to save your managed object context if you want to keep these changes for when the app closes.
NSArray* myCollection = [[self fetchedResultsController] fetchedObjects];
for(ActiveListData *managedObject in myCollection)
{
if(managedObject != nil && managedObject.isChecked)
{
managedObject.isChecked = NO;
managedObject.isActive = NO;
}
}
If you want to do the check on all object in the database you'll need a new method in your NSFetchedResultsController that has a predicate checking on isChecked and then loops over and edits the result collection.
You might want to post your error code as we could be able to point out what you did wrong.
Edit: If you're not familiar with using Core Data the apple documentation provides a lot of information: http://developer.apple.com/library/mac/#documentation/cocoa/Conceptual/CoreData/Articles/cdBasics.html
Thanks to #ggfela for his answer. The processes of his answer were spot on. Here is the actual code I put into my button/method, in hopes of it helping someone else in the future:
NSArray *moc = [[self fetchedResultsController] fetchedObjects];
for (ActiveListData *item in moc) {
if (item != nil && item.isChecked.boolValue == 1) {
item.isChecked = [NSNumber numberWithBool:NO];
item.isActive = [NSNumber numberWithBool:NO];
}
}
// Call to Data Store to update the list
NSError *error;
if (![self.managedObjectContext save:&error]) {
FATAL_CORE_DATA_ERROR(error);
return;
Explanation:
Load the contents of the result from calling the fetchedResultsController method into a temporary variable named moc
Use a for loop to cycle through the array of moc. ActiveListData is the NSManagedObject subclass that I created for my Core Data and is the proper place to insert the separated values/attributes from the data store. From there, it's pretty simple, I ensure that item is not nil AND that the item's attribute is the value I need.
NOTE
Core Data does not store the bool values YES and NO but rather 1 and 0, respectively but when you call or compare the values, you simply can not compare the value of item.isChecked because it is being passed back to you as a bool not as an integer. You can not simply compare item.isChecked == YES either since the #property of isChecked is an NSNumber. So, in the case of the if I put item.isChecked.boolValue as this will give me the representing integer for it's bool value, in this case I have it check for a 1 (YES). (Sorry if my explanation is wrong and/or confusing, but this is how I understand it and is the only way this code works.)
Then, setting the new values of these attributes is like you would expect when setting any other variable. The only "tricky" difference with this is that the NSManagedObject subclass sets the #property of the isChecked and isActive to an NSNumber (as mentioned earlier) so in order to send the proper values back to Core Data you use the method numberWithBool of the NSNumber class.
And just in case anyone gets confused by my FATAL_CORE_DATA_ERROR(error) call this is simply a macro that was defined inside the Prefix.pch file to handle my errors from the managedObjectContext. You can use any (or none) error handling you choose.
Thanks again #ggfela for your help!! If anyone else has any other suggestions on how this code should be applied, then please let me know!
You can use NSBatchUpdateRequest to update multiple records
Examples:
https://www.bignerdranch.com/blog/new-in-core-data-and-ios-8-batch-updating/
http://code.tutsplus.com/tutorials/ios-8-core-data-and-batch-updates--cms-22164

Calling ALAssetReprsentation's metadata method several hundred times will fail

I am writing a tiny iPhone app to retrieve metadata, such as EXIF info, for all photos stored in the iPhone, and ran into a weird issue when calling the Assets Library Framework API. Basically, if I am calling ALAssetReprsentation's metadata method (http://developer.apple.com/library/ios/documentation/AssetsLibrary/Reference/ALAssetRepresentation_Class/Reference/Reference.html#//apple_ref/occ/instm/ALAssetRepresentation/metadata) for several hundred times (even for the same ALAssetReprsentation object), the API will report an error and return null instead of photo's metadata.
Here is the code to reproduce this issue:
ALAsset *photo = ... // fetch a photo asset via Assets Library Framework
int i = 0;
ALAssetRepresentation *representation = [photo defaultRepresentation];
NSDictionary *metadata;
while (i<600) {
i++;
metadata = [representation metadata];
NSLog(#"photo %d indexed %#", i, metadata);
}
Here is the output for the code above. In the beginning of the output, everything is okay, but after 500+ times, the metadata API will report error like "ImageIO: CGImageSourceCreateWithData data parameter is nil".
...
2011-12-29 21:46:17.106 MyApp[685:707] photo 578 indexed {
ColorModel = RGB;
DPIHeight = 72;
DPIWidth = 72;
...
}
...
ImageIO: <ERROR> CGImageSourceCreateWithData data parameter is nil
2011-12-29 21:46:17.151 MyApp[685:707] photo 579 indexed (null)
ImageIO: <ERROR> CGImageSourceCreateWithData data parameter is nil
2011-12-29 21:46:17.177 MyApp[685:707] photo 580 indexed (null)
I am testing in an iPhone 3GS with iOS 5.0.1. And I am developing with Xcode 4.2 with ARC (automatical reference counting) enabled. And I can only reproduce this issue when deploying the app to the iPhone 3GS device, but cannot reproduce this issue when using iOS simulator with the same code (at least I don't reproduce this issue after calling the API over 1800 times in iOS simulator).
Any help is appreciated. Thanks.
It is possible that you are running out of memory. The method [representation metadata] returns an autoreleased object and possibly creates more autoreleased objects when it executes. All these instances are added to the autorelease pool, waiting to be finally released (and their memory freed) when the ARP gets the chance to drain itself.
The problem is that this won't happen until your code returns control to the run loop. So for the duration of your loop, at least 600 large dictionaries (and possibly many more objects) end up being allocated and not deallocated. Depending on the size of these objects, memory usage can increase tremendously.
This is true whether you are using ARC or not.
To avoid this issue, try creating a fresh autorelease pool on every iteration of the loop. That way, the ARP gets drained on every iteration:
while (i<600) {
#autoreleasepool {
i++;
metadata = [representation metadata];
NSLog(#"photo %d indexed %#", i, metadata);
}
}
This is not necessarily the best solution from a performance perspective but at least it will tell you whether the problem is memory related.
PS: Your code doesn't make much sense at the moment. Why retrieve the metadata for the same asset 600 times in a row?
enter code hereMake sure you retain the ALAssetLibrary until you are finished access related assets. From Apple's docs:
The lifetimes of objects you get back from a library instance are tied
to the lifetime of the library instance.

Proper Management Of A Singleton Data Store In IOS With Web Service

I'm currently using a singleton as a data store for my app. I essentially store a number of events that are pulled and parsed from a web service and then added as needed. Each time I make a request from the web service, I parse the results and see if the items already exist. If they do, I delete them and add the updated version provided by the web service.
Everything appeared to be working properly until I fired up the Instruments panel to find out that my system is leaking the objects every time it loads them from the web service (from the second time on). The core method where things appear to be messing up is this one, which is located in my HollerStore singleton class:
- (void)addHoller: (Holler *)h
{
//Take a holler, check to see if one like it already exists
int i = 0;
NSArray *theHollers = [NSArray arrayWithArray:allHollers];
for( Holler *th in theHollers )
{
if( [[th hollerId]isEqualToString:[h hollerId]] )
{
NSLog(#"Removing holler at index %i", i);
[allHollers removeObjectAtIndex:i];
}
i++;
}
[allHollers addObject:h];
}
Quick explanation: I decided to copy the allHollers NSMutableArray into theHollers because it's being updated asynchronously by NSURLConnection. If I update it directly, it results in a crash. As such, I switched to this model hoping to solve the problem, however the Instruments panel is telling me that my objects are leaking. All the counts are exactly the # of items I have in my data set.
From what I can tell removeObjectAtIndex isn't effectively removing the items. Would love to get the thoughts of anybody else out there on three things:
Is my analysis correct that something else must be retaining the individual hollers being added?
Should I be using CoreData or SQLite for storing information pulled from the web service?
Do you know how long data stored in a Singleton should be available for? Until the app is killed?
Update
I think I've found the source, however perhaps someone can provide some clarity on the proper way to do this. I've created a method called parseHoller which takes a dictionary object created through SBJSON and returns my own model (Holler). Here are the last couple lines:
Holler *h = [[[Holler alloc] initFromApiResponse:hollerId
creatorId:creatorId
creatorName:creatorName
creatorImageUrl:creatorImage
comments:comments
attendees:attendees
wishes:wishes
invitees:invites
createdAt:createdAt
text:text
title:title
when:when]autorelease];
//(some other autorelease stuff is here to clean up the internal method)
return h;
I figured that since I'm returning an autoreleased object, this should be fine. Do you see anything wrong with this?
Have you tried to do a retain count on the objects that is leaking? Maybe that could clear up when or where it is being retained.
The code should be
[putObjectHere retainCount];
and then write to an NSLog
Hope it gives you something
Peter

EXC_MEMORY_ACCESS when trying to delete from Core Data ($cash solution)

I have an application that downloads an xml file, parses the file, and creates core data objects while doing so. In the parse code I have a function called 'emptydatacontext' that removes all items from Core Data before creating replacements items from the xml data. This method looks like this:
-(void) emptyDataContext
{
NSFetchRequest * allCon = [[NSFetchRequest alloc] init];
[allCon setEntity:[NSEntityDescription entityForName:#"Condition" inManagedObjectContext:managedObjectContext]];
NSError * error = nil;
NSArray * conditions = [managedObjectContext executeFetchRequest:allCon error:&error];
DebugLog(#"ERROR: %#",error);
DebugLog(#"RETRIEVED: %#", conditions);
[allCon release];
for (NSManagedObject * condition in conditions) {
[managedObjectContext deleteObject:condition];
}
// Update the data model effectivly removing the objects we removed above.
//NSError *error;
if (![managedObjectContext save:&error]) {
DebugLog(#"%#", [error domain]);
}
}
The first time this runs it deletes all objects and functions as it should - creating new objects from the xml file. I created a 'update' button that starts the exact same process of retrieving the file the proceeding with the parse & build. All is well until its time to delete the core data objects. This 'deleteObject' call creates a "EXC_BAD_ACCESS" error each time. This only happens on the second time through.
Captured errors return null. If I log the 'conditions' array I get a list of NSManagedObjects on the first run. On the second this log request causes a crash exactly as the deleteObject call does.
I have a feeling it is something very simple I'm missing or not doing correctly to cause this behavior. The data works great on my tableviews - its only when trying to update I get the crashes.
I have spent days & days on this trying numerous alternative methods. Whats left of my hair is falling out. I'd be willing to ante up some cash for anyone willing to look at my code and see what I'm doing wrong. Just need to get past this hurdle.
Thanks in advance for the help!
Did you save the context after removing the objects in the for loop? Be aware that deleteObject: does not delete the object immediately, it simply schedule it for deletion when changes are committed, i.e., when you save the context.
EDIT: Your problem may be related to how you present your data to the user on your table view. Without additional code is difficult to tell exactly (are you using NSFetchedResultsController or not?), but my guess is that the interaction between deleting the data and showing them on the table is not correct. Probably, what is happening is that your table is told to visualize your data, but then, when you delete them, you are not updating correctly the table.
Wow, so after a few days of testing I went down a path that led me to tracking down Zombies & memory mgmt. This was a situation where the errors I was recieving were leftover from issues in another area. When parsing the data and placing it into Core Data I released an object that was to be autoreleased. So any subsequent calls to that item within core data (save, deletec, etc) caused a crash. Thank you all for your help and I vow to pay closer attention with my memory mgmt calls.

Memory efficient way of inserting an array of objects with Core Data

I'm working on a piece of code for an iPhone application that fetches a bunch of data from a server and builds objects from it on the client. It ends up creating roughly 40,000 objects. They aren't displayed to the user, I just need to create instances of NSManagedObject and store them to persistent storage.
Am I wrong in thinking that the only way to do this is to create a single object, then save the context? is it best to create the objects all at once, then somehow save them to the context after they're created and stored in some set or array? If so, can one show some example code for how this is done or point me in the direction to code where this is done?
The objects themselves are relatively straight forward models with string or integer attributes and don't contain any complex relationships.
In any case, don't save after inserting every object, or be prepared for dreadful performances.
Here is the code I use to populate a Core Data repository upon first launch.
#define MAX_UNSAVED_AIRPORTS_BEFORE_SAVE 1000
int numAirports = 0;
int numUnsavedAirports = MAX_UNSAVED_AIRPORTS_BEFORE_SAVE; // *** bug. see below
for (NSDictionary *anAirport in initialAirports) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Airport *newAirport = [NSEntityDescription insertNewObjectForEntityForName:#"Airport" inManagedObjectContext:managedObjectContext];
newAirport.city = [anAirport objectForKey:#"city"];
newAirport.code = [anAirport objectForKey:#"code"];
newAirport.name = [anAirport objectForKey:#"name"];
newAirport.country_name = [anAirport objectForKey:#"country_name"];
newAirport.latitude = [NSNumber numberWithDouble:[[anAirport objectForKey:#"latitude"] doubleValue]];
newAirport.longitude = [NSNumber numberWithDouble:[[anAirport objectForKey:#"longitude"] doubleValue]];
newAirport.altitude = [NSNumber numberWithDouble:[[anAirport objectForKey:#"altitude"] doubleValue]];
numAirports++;
numUnsavedAirports++;
if (numUnsavedAirports >= MAX_UNSAVED_AIRPORTS_BEFORE_SAVE) {
if (![managedObjectContext save:&error]) {
NSLog(#"Unresolved error %#, %#", error, [error userInfo]);
abort();
}
numUnsavedAirports = 0;
}
[pool release];
}
Also don't forget to save one last time after the loop.
Also be aware that a bug exists that will lead to a crash if all three of the following conditions are met:
The Repository is empty
You have a UITableView with sections
Your first save saves more than one object.
The workaround in the code above is to initialize the numUnsavedAirports to MAX_UNSAVED_AIRPORTS_BEFORE_SAVE in order to make sure the first save happens after the first insert.
I hope this helps.
Saving after each object would produce very bad performance. You should have a balance of the saves perhaps every 100 (testing will determine the sweet spot) and then keep track of where you are at in the processing when the user quits.
You get time on exit to store state so you can easily store your position in the data processing (5 blocks of 100 saved) and pick back up where you left off.
Saving every object individually would hammer the disk and slow the app to a crawl.
It's probably better to create a single object and save the context.
You have 40k objects. Let's say that creating a single NSManagedObject takes x time units. 40kx time units is probably measurable. While the object creation is happening, the user may quit the app for some reason; users are unpredictable. The next time your app starts, you go through the process all over again. It would not be desirable to create the 39,999th object only to have the user quit the app and lose all that work.
If your app were to create each object and save, you could speed up this process a bit. The app starts up and checks to see if it was able to complete the task the last time it ran. If the task was incomplete, it could try to pick up where it left off.
The single object creation and save method may take a longer time to complete but will have a greater likelihood of completing the task.
In terms of memory consumption, this also minimizes the in memory state of your app. The context isn't tracking 40k objects in memory.