I have a really annoying problem, which I just can't seem to get fixed.
I have a view when I send a message that gets saved to the Core Data, when thats done it asked the database for a random message (sentence) and saved that as well to an other row in the database.
If I do the last part hardcoded, without fetching data from the DB, it works all fine and dandy, but as soon as I fetch the random row from the DB it goes crazy.
In my AppDelegate.m:
- (void)save {
NSAssert(self.context != nil, @"Not initialized");
NSError *error = nil;
BOOL failed = [self.context hasChanges] && ![self.context save:&error];
NSAssert1(!failed,@"Save failed %@",[error userInfo]);
}
- (NSString*)selectRandomSentence
{
NSFetchRequest *request = [[NSFetchRequest alloc] init];
NSEntityDescription *entity = [NSEntityDescription entityForName:@"Sentences" inManagedObjectContext:self.managedObjectContext];
[request setEntity:entity];
NSError *error = nil;
NSUInteger count = [self.context countForFetchRequest:request error:&error];
NSUInteger offset = count - (arc4random() % count);
[request setFetchOffset:offset];
[request setFetchLimit:1];
NSArray *sentenceArray = [self.context executeFetchRequest:request error:&error];
[request release];
return [[sentenceArray objectAtIndex:0] sentence];
}
- (NSManagedObjectContext *)context {
if (_managedObjectContext != nil)
return _managedObjectContext;
NSPersistentStoreCoordinator *coordinator = [self coordinator];
if (coordinator != nil) {
_managedObjectContext = [[NSManagedObjectContext alloc] init];
[_managedObjectContext setPersistentStoreCoordinator:coordinator];
}
return _managedObjectContext;
}
In my ChatController.m:
- (void)didRecieveMessage:(NSString *)message
{
[self addMessage:message fromMe:NO];
}
#pragma mark -
#pragma mark SendControllerDelegate
- (void)didSendMessage:(NSString*)text {
[self addMessage:text fromMe:YES];
}
#pragma mark -
#pragma mark Private methods
- (void)responseReceived:(NSString*)response {
[self addMessage:response fromMe:NO];
}
- (void)addMessage:(NSString*)text fromMe:(BOOL)fromMe {
NSAssert(self.repository != nil, @"Not initialized");
Message *msg = [self.repository messageForBuddy:self.buddy];
msg.text = text;
msg.fromMe = fromMe;
if (fromMe)
{
[self.bot talkWithBot:text];
}
[self.repository asyncSave];
[self.tableView reloadData];
[self.tableView scrollToRowAtIndexPath:[NSIndexPath indexPathForRow:0 inSection:[self.buddy.messages count] - 1] atScrollPosition:UITableViewScrollPositionBottom animated:YES];
}
In My OfflineBot.m:
- (void)talkWithBot:(NSString *)textFromMe
{
AppDelegate *delegate = [[UIApplication sharedApplication] delegate];
[self didRecieveMessage:[delegate selectRandomSentence]];
}
- (void)didRecieveMessage:(NSString *)message
{
if ([self.delegate respondsToSelector:@selector(didRecieveMessage:)])
[self.delegate didRecieveMessage:message];
}
Repository.m
- (Message*)messageForBuddy:(Buddy*)buddy {
Message *msg = [self.delegate entityForName:@"Message"];
msg.source = buddy;
[self.delegate.managedObjectContext refreshObject:buddy mergeChanges:YES];
return msg;
}
- (void)asyncSave {
[self.delegate save];
}
The error:
2012-08-10 00:28:20.526 Chat[13170:c07] * Assertion failure in -[AppDelegate save], /Users/paulp/Desktop/TestTask/Classes/AppDelegate.m:28 2012-08-10 00:28:20.527 Chat[13170:c07] * Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Save failed {type = immutable dict, count = 2, entries => 1 : {contents = "NSAffectedObjectsErrorKey"} = ( " (entity: Sentences; id: 0x6b8bf10 ; data: )" ) 2 : {contents = "NSUnderlyingException"} = CoreData could not fulfill a fault for '0x6b8bf10 ' }
What am I doing wrong?
Update I pinpointed the error to this row:
NSArray *sentenceArray = [self.context executeFetchRequest:request error:&error];
When I execute that row, I get the error... that is when fetching the data. The error, however, seems to turn up when saving the new data to the Messages entity. The random sentence is fetched from Sentences.
After I changed the asyncSave method to saving directly (thus not using a new thread) it saves the first chat, but nothing after that. It dies.
Update
It all seems to work using this in my didFinishLaunchingWithOptions
:
[self.context setRetainsRegisteredObjects:YES];
I understand that hereby the CodeData Object Model Context doesn't release its objects, which seems to be the problem betweens adding and saving. But why?
executeFetchRequest
call yet your error occurs while saving your context which doesn't make sense. Any chance you use multiple threads in your application? That is very often culprit forNSInternalInconsistencyException
– Clarksonarc4random() % count
, you can usearc4random_uniform( count )
. It's more correct, not that it matters. – Alanassert( [ NSThread currentThread ] == [ NSThread mainThread ] )
– Alancount - 1 - arc4random() % count
– Alan