I am getting a weird crash when I try to save my model. This is my code:
TJModel *model = [TJModel sharedTJModel];
NSFetchRequest *request = [[[NSFetchRequest alloc] init]autorelease];
[request setReturnsObjectsAsFaults:NO];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"TJVideoList"inManagedObjectContext:[model managedObjectContext]];
[request setEntity:entity];
NSError *error = nil;
NSMutableArray *mutableFetchResults = [[[model managedObjectContext] executeFetchRequest:request error:&error] mutableCopy];
if (error != nil)
NSLog(#"error %#",[error localizedDescription]);
TJVideoList *videoList = nil;
if ([mutableFetchResults count] == 0) {
videoList = (VideoList *)[NSEntityDescription insertNewObjectForEntityForName:#"TJVideoList"
inManagedObjectContext:[model managedObjectContext]];
}
else
{
videoList = [mutableFetchResults objectAtIndex:0];
}
[videoList addVideoListObject:recordedVideo];
error = nil;
if (![[model managedObjectContext] save:&error]) {
And crash .....This is what said in the terminal:
-[NSConcreteValue UTF8String]: unrecognized selector sent to instance 0x1d33f0
I thought it might be a cuestion of deallocated objects, so I retained them like this:
[managedObjectContext setRetainsRegisteredObjects:YES];
With no luck.
Your crash is not a result of this code.
Crashes in saves usually result from an error with an managedObject's attributes. In this case, you have somewhere assigned the wrong value to a string attribute. When the context goes to convert the string attribute to a UTF8 string for persistence, the object that is there instead of the NSString does not understand the message and the crash results.
Although this code should run okay, you do have some risky practices:
NSFetchRequest *request = [[[NSFetchRequest alloc] init]autorelease];
This is a bad practice. autorelease is the same as release. You should not send it to an object until you are complete done with it. autorelease marks an object for death the next time the memory pool is drained. In some case, that will kill the object unexpectedly. While it won't cause problems here, you don't want to get into the habit of taking this short cut because it will eventually bite you.
You should only use autorelease when the current scope is done with the object but the object is being sent outside the scope (usually in a method return.)
NSMutableArray *mutableFetchResults = [[[model managedObjectContext] executeFetchRequest:request error:&error] mutableCopy];
The mutable array here is pointless as is the copy. This is apparently in some reference material somewhere because it keeps cropping up novices code in the last few months. If you're not going to alter an array there is no reason to have it mutable. In the case of an array of managed objects, it is pointless to copy the array.
videoList = [mutableFetchResults objectAtIndex:0]
Since you have no sort descriptor for the fetch, the mutableFetchResults array will be in a random order. If you have more than one object returned, which is almost always the case, you will get a random TJVideoList object at the zero element every time you run the code.
Sounds more like you assigned an NSValue instance (NSNumber, most likely, as it is the most commonly used subclass) where an NSString was expected. The -retainsRegisteredObjects: is unlikely to be needed (it won't fix a memory related problem anyway).
It might possibly be an over-release issue, too. Try running with Zombie detection enabled (see the Run menu).
Related
Okay, I'm having a problem saving after I've deleted all the objects I have stored in CoreData. I have no problem with saving if I don't delete anything, but as soon as I ask it to delete all the objects (everything deletes with no errors or problems), and then try saving again, it crashes and just gives me a program received signal: SIGABRT. Here's my code.
- (void)deleteStoredData
{
// Define our table/entity to use
NSEntityDescription *entity = [NSEntityDescription entityForName:MOVIE_LIST inManagedObjectContext:managedObjectContext];
// Setup the fetch request
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:entity];
// Fetch the records and handle an error
NSError *error;
NSUInteger count = [managedObjectContext countForFetchRequest:request error:&error];
if (count) {
for (int i = 0; i < count - 1; i++) {
NSManagedObject *eventToDelete = [self.listOfMovies objectAtIndex:i];
[managedObjectContext deleteObject:eventToDelete];
}
}
[request release];
}
I thought it might be me comparing self.listOfMovies to the objects stored, so I did a fresh fetch, copied it to a temp NSMutableArray, then replaced self.listOfMovies with temp. But no changes, still crashes. Did I some how delete the entire record and it no longer exists?
What I want to do is load everything onto the app, then delete all the objects in CoreData, so that when the app closes (or terminates) it saves all the new data in the records. Am I doing this correctly, or is there a much easier way to do this? Oh yea, and I only have one entity that holds 5 NSStrings, so nothing to complicated.
Thanks in advance everyone.
Possibly, when you call the save method, there might be some mixup with some variable such as the managedObjectContext.
Did you try saving right after the deletion (i.e. in your deleteStoredData method above)?
BTW, I would also go with Christopher's code;-).
Deleting managed objects with a for loop like that is error prone and probably corrupting your managedObjectContext. Try the following:
NSFetchRequest * fetch = [[[NSFetchRequest alloc] init] autorelease];
[fetch setEntity:[NSEntityDescription entityForName:MOVIE_LIST inManagedObjectContext:context]];
NSArray * result = [context executeFetchRequest:fetch error:nil];
for (NSManagedObject * event in result) {
[context deleteObject:event];
}
Since you are already have all of managedObject in your array, you do NOT need to do another fetch, just delete them with the code below should be OK.
for (NSUInteger i = 0; i < [self.listOfMovies count] - 1; i++) {
NSManagedObject *eventToDelete = [self.listOfMovies objectAtIndex:i];
[managedObjectContext deleteObject:eventToDelete];
}
If it still have problem, would you please tell me how many managedObjectContext do you have in your App? Are you deleting or saving it in a background thread?
In addition, would you please also post the crash log and the information by type "bt -> enter key" in console after your App crashed?
My guess is I'm missing some Core Data fundamental understanding here, but here goes:
I have several fetch requests in my app to handle retrieval of different things. In some cases the code runs fine, returning the requested objects.
In some cases, it returns what appear to be already-released objects (e.g. just a few lines of code later trying to reference the returned result gives EXC_BAD_ACCESS). As I set various breakpoints and log statements in the code and step through it will also get the occasional SIGABRT or EXC_BAD_ACCESS in other locations in the code.
In every case, it appears to be when I go to reference the result of a fetch request.
Here's a sample of one such fetch request:
// Who am I?
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSString *signedInPersonId = [defaults stringForKey:#"signedInPersonId"];
// Return (if any) the Request object with given UUID
RequestStrings *r = [[RequestStrings alloc] init];
NSEntityDescription *description = [NSEntityDescription entityForName:r.table_Request inManagedObjectContext:moc];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(UUID == %#) && (dataOwnerId == %#)", UUID, signedInPersonId];
NSFetchRequest *request = [[NSFetchRequest alloc] init];
[request setEntity:description];
[request setPredicate:predicate];
NSError *error = nil;
NSArray *requests = [moc executeFetchRequest:request error:&error];
Request *returnRequest = nil;
if (requests != nil) {
if ([requests count] > 0) {
NSLog(#"getRequestWithId - requests array: %#, first: %#", requests, [requests objectAtIndex:0]);
returnRequest = [requests objectAtIndex:0];
}
else {
returnRequest = nil;
}
}
[r release];
[request release];
return returnRequest;
P.S. Here's some more info
In some instances the same code will return the desired objects, or throw an exception stating that [NSCFNumber length] is an unrecognized selector. Not sure how the same entity description + fetch request could return an array in one case and a Number in the other.
You're basically doing this :
// (1) Create an array of stuff
NSArray *myArray = [NSarray arrayWithObjects:a, b, c, nil];
// (2) Take the first one off
id myObject = [myArray objectAtIndex:0];
// (3) Release everything
[myArray release];
// (4) Return myObject
return myObject;
You're just using CoreData to do step (1).
Step (1) returns an array of objects. The only thing retaining these objects is the array that they're in*. If you release the array (step 3), you will release all the objects that are inside it. Why do you expect myObject to still exist by step (4)?
Try this :
// Make sure that we keep a retain of returnRequest
returnRequest = [[[requests objectAtIndex:0] retain] autorelease];
*For the pedantic out there : I've made an assumption that makes my answer simpler. In the real world you don't know what's retaining your objects - that's up to the framework as well as your code. However, it's good practice to retain anything you expect to keep around.
Why don't you do all the releases at the end before the return? This should fix your issue as you're releasing things immediately after creating them.
#deanWombourne - good suggestions, and I think you're right on. It turns out I actually was crashing from a zombie (wasn't using my own setters in a previous view controller when I was using values from a previously fetched managed object).
The solution: EXC_BAD_ACCESS is always worth a trip down zombie lane. Do your due diligence on memory management.
I'm getting this weird error from Core Date and I cant understand why.
The code below is executed when I delete a row of a UITableView.
I pass a string and an object to the method below and it fetches the article in a database table that has that string and has a foreign key to that object. Then I delete that object and reload the table.
- (void)deleteFavorite:(NSString *)link inFolder:(Favorites *)f {
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *favsDecriptor = [NSEntityDescription entityForName:#"Favorites" inManagedObjectContext:context];
[request setEntity:favsDecriptor];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"(belongsTo == %#) AND (link = %#)", f, link];
[request setPredicate:predicate];
NSError *error = nil;
NSMutableArray *fav = [[NSMutableArray alloc] init];
fav = [[context executeFetchRequest:request error:&error] retain];
if (![context save:&error]) {
NSLog(#"Cannot fetch the story from the fetch request.");
}
NSLog([[fav objectAtIndex:0] title]);
error = nil;
[context deleteObject:[fav objectAtIndex:0]];
if (![context save:&error]) {
NSLog(#"Can't delete the fav! %#", error);
}
}
The app instantly crashes and I get this message in the console.
But when I launch the app afterwards, the row has been deleted.
Detected an attempt to call a symbol in system libraries that is not present on the iPhone:
_Unwind_Resume called from function _PFFaultHandlerLookupRow in image CoreData.
Please help!
Thanks in advance to everyone!
This is probably related to a bug within Core Data itself. I had the same error come up (I asked about it here in SO) and my only fix was to change the keywords in the predicate that still allowed the same results. It took some experimenting to find the right combination. Not ideal, but that's the best answer I can offer based on my experience.
Is it possible that you are holding a reference to the delete object or that the deleted object is an observer and is getting a callback after its been deleted? I had something similar to this recently, though slightly different error message. In my case, I also crashed upon deletion (under some conditions) but when I relaunched the object-to-be-deleted had, in fact, been deleted.
If you haven't already done so, under the Run menu select Stop on Objective-C Exceptions. This helped me track down the root cause of my crash. In my case it was KVO observer getting callback of change of value of a property of deleted NSManagedObject.
In the following code:
- (NSMutableArray *) fetchNotesForGroup: (NSString *)groupName {
// Variables declaration
NSMutableArray *result;
NSFetchRequest *fetchRequest;
NSEntityDescription *entity;
NSSortDescriptor *sortDescriptor;
NSPredicate *searchPredicate;
NSError *error = nil;
// Creates the fetchRequest and executes it
fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
entity = [NSEntityDescription entityForName:#"Note" inManagedObjectContext:managedObjectContext];
[fetchRequest setEntity:entity];
sortDescriptor = [[[NSSortDescriptor alloc] initWithKey:#"noteName" ascending:YES] autorelease];
[fetchRequest setSortDescriptors:[NSArray arrayWithObject:sortDescriptor]];
[fetchRequest setReturnsDistinctResults:YES];
searchPredicate = [NSPredicate predicateWithFormat:#"categoryName like %#", groupName];
[fetchRequest setPredicate:searchPredicate];
[fetchRequest setPropertiesToFetch:[NSArray arrayWithObject:#"noteName"]];
result = [[managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
// Variables release
return result;
}
... I Fetch notes for a given categoryName. When I'm running Instruments, it says that a NSCFString is leaking.
I know leaks are mean for iPhone developers... but I don't have any idea on how to plug this one.
Any clues? All help is welcome.
Thanks a lot!
Your problem is this:
result = [[managedObjectContext executeFetchRequest:fetchRequest error:&error] mutableCopy];
// Variables release
return result;
mutableCopy returns an owning reference (ie, an object with a +1 retain count), which you are responsible for (auto)releasing. You don't, and you then relinquish the reference, which means you've leaked the array.
Use return [result autorelease]; instead.
First of all, Instruments may not be always accurate.
It can report leaks on some special case, just because it don't see you are actually releasing the object elsewhere.
It's also possible that some parts of the CF have leaks.
What I can see in your code is that you are using auto-released objects.
You're temporary objects will have a long life-cycle.
What append if you release them explicitly instead, just before the method's return?
You are also returning a copy of an object.
You have to make sure that object is released at some point.
It may be much better to return an auto-released object, and let the calling method decide if the object should be retained.
Since you don't create any strings, the leaking string is most likely one of the strings that ends up inside the fetch request and it is most likely the fetch request that is actually leaking. (The call stack shown in Instruments should confirm this.)
I don't think you have an actual leak but by doing this:
fetchRequest = [[[NSFetchRequest alloc] init] autorelease];
... you are allowing the fetchRequest to live beyond the scope where it was defined Instruments will interpret that as a leak.
Autorelease actually makes objects live longer than direct release because it causes objects to hang around until the outer autorelease pool is drained. For example, if you create object-B inside object-A and return it to object-C autoreleased, object-B will stay alive long after object-A has been deallocated even if object-C never retains it. (Although it will eventually die at an unpredictable moment.)
Autorelease is not a convenience method for retaining. It has a specific purpose of retaining objects that are being passed between other objects. If your not doing this, don't use autorelease.
If you do this:
fetchRequest = [[NSFetchRequest alloc] init];
// ...
[fetchRequest release];
... your leak will go away.
You might, however, want to do this:
return [result autorelease];
... to ensure that the result array lives long enough to be retained by another object.
I have a ConfiguracaoDaApp class in my project that is a NSManagedObject subclass. I didn't change the default code that XCode generates.
I declare a instance variable of that type in my app delegate and in my appDidFinishLaunching method, I have been try to assign it's value from a object retrieved from the database like this:
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:#"ConfiguracaoDaApp" inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
configDaApp = [[managedObjectContext executeFetchRequest:request error:&error] objectAtIndex:0];
The problem is that the line
[[managedObjectContext executeFetchRequest:request error:&error] objectAtIndex:0];
don't returns a object of the type ConfiguracaoDaApp.
I tried change the line to this:
configDaApp = [[[managedObjectContext executeFetchRequest:request error:&error] objectAtIndex:0] entity];
Then a NSEntityDescriptor is returned and the problem remains the same.
So, my question is: how to retrieve a real business object from a executeFetchRequest?
Thanks in advance.
Obs: forgive me if it is a beginner question but is my first iPhone app.
I just figure out what is happening:
[[managedObjectContext executeFetchRequest:request error:&error] objectAtIndex:0];
returns a autorelease object and when I tried to access the property in other parts of the code, its contents was memory trash of the application because the original ConfiguracaoDaApp object was released by autorelease pool. My property was declared with retain, but the object is autoreleased anyway. So I putted explicitly a retain in the line:
configDaApp = [[[managedObjectContext executeFetchRequest:request error:&error] objectAtIndex:0] retain];
Then everything works fine.
Thanks anyway, guys.