Re: NSObjectInaccessibleException -- CoreData could not fulltill a fault
Re: NSObjectInaccessibleException -- CoreData could not fulltill a fault
- Subject: Re: NSObjectInaccessibleException -- CoreData could not fulltill a fault
- From: Steve Steinitz <email@hidden>
- Date: Fri, 5 Oct 2007 19:35:13 +1000
Hello,
When an NSObjectInaccessibleException occurs, there is a chance that
the developer will be in a mild state of panic -- especially with a
live database. So, for what its worth, I'll share the crude tool I
used to get out of hot water.
I made a new class called, say, DBOrphanRemover.
I added a method called deleteOrphanedObjects
deleteOrphanedObjects loops through all of the objects in the entities
(line items and payments) which I suspect to contain orphans (have
no sale or can't fulfill a sale fault) and deletes the ones with nil
sale. Note that it puts the line item from each iteration into an
instance variable called currentObject. I'll explain why below.
NSFetchRequest *lineItemRequest = [[NSFetchRequest alloc] init];
[lineItemRequest setEntity: [NSEntityDescription entityForName: @"LineItem"
inManagedObjectContext: context ]];
NSError *error = nil;
NSArray *lineItems = [context executeFetchRequest: lineItemRequest
error: &error];
[lineItemRequest release];
NSEnumerator * lineItemEnumerator = [lineItems objectEnumerator];
Sale * sale;
while (currentObject = [lineItemEnumerator nextObject])
{
sale = [(LineItem *) currentObject sale];
if (nil == sale ||
nil == [sale invoiceNumber])
{
[context deleteObject: currentObject];
NSLog (@"deleted lineItem with null sale");
}
}
I more or less repeated that code for payments.
However, that isn't the full story. While that code did delete some
of the orphaned objects, there were others that generated fault
fullfillment exceptions, stopped looping and so didn't examine further
objects.
So, to delete the excepting objects DBOrphanRemover had to catch an
exception and delete the object which was conveniently saved in
currentObject. (Sound foolhardy enough?) Important: after each
exception, I re-run the above loop again and again, by clicking a
'Delete Orphans' button, until finally, it finds no more orphaned
objects and all is hopefully well with the database. I am too timid
to make it re-run automatically.
There are a few steps to handling the exception.
deleteOrphanedObjects saves the current exception handler delegate and
sets its DBOrphanRemover instance (self) as the delegate.
id exceptionHandlerDelegate =
[[NSExceptionHandler defaultExceptionHandler] delegate];
[[NSExceptionHandler defaultExceptionHandler] setDelegate: self];
There are also a number of methods that need implementations for a
class handle exceptions. I've shown my simple implementations at the
end of this post.
Here is the method that handles the exception. My naivety of
exception handling nuances will be apparent to those of you having
more experience with them.
- (BOOL)
exceptionHandler: (NSExceptionHandler *) sender
shouldHandleException: (NSException *) exception
mask: (unsigned int) aMask
{
if (nil != currentObject)
{
[context deleteObject: currentObject];
NSLog (@"deleting Object that caused an exception");
}
return NO;
}
At the end of the process, deleteOrphanedObject restores the original
exception handler delegate:
[[NSExceptionHandler defaultExceptionHandler]
setDelegate: exceptionHandlerDelegate];
I confess that this technique is full of danger and is so roughly hewn
that it may never be able to be made sound -- but, it got me out of hot
water and didn't appear to cause any problems.
I welcome comments and improvements.
Steve
Three other methods that need implementatios for exception handling:
// filter out some common, harmless exceptions
- (BOOL)
shouldDisplayException: (NSException *) exception
{
NSString* name = [exception name];
NSString* reason = [exception reason];
if ( [name isEqualToString: @"NSImageCacheException"] ||
[name isEqualToString: @"GIFReadingException"] ||
[name isEqualToString: @"NSRTFException"] ||
([name isEqualToString: @"NSInternalInconsistencyException"] &&
[reason hasPrefix: @"lockFocus"])
)
{
return NO;
}
return YES;
}
- (BOOL)
exceptionHandler: (NSExceptionHandler *) sender
shouldLogException: (NSException *) exception
mask: (unsigned int) aMask
{
// controls whether the exception shows up in the console, usually return YES
return YES;
}
- (id)
infoValueForKey: (NSString*) key
{
id result;
if ([[[NSBundle mainBundle] localizedInfoDictionary] objectForKey:key])
{
result = [[[NSBundle mainBundle] localizedInfoDictionary] objectForKey:key];
}
else
{
result = [[[NSBundle mainBundle] infoDictionary] objectForKey:key];
}
return result;
}
---
Steve Steinitz ph +61 (0)2 9487 7215
Director
Data Tactics
Sydney, Australia
www.datatactics.com.au
Web Commerce Development
Project Estimation and Planning
Software Development
MacOS X Support
_______________________________________________
Cocoa-dev mailing list (email@hidden)
Please do not post admin requests or moderator comments to the list.
Contact the moderators at cocoa-dev-admins(at)lists.apple.com
Help/Unsubscribe/Update your Subscription:
This email sent to email@hidden