Re: NSObjectInaccessibleException -- CoreData could not fulltill a fault

  • 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
previous month october 2007 next month
MTWTFSS
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
Go to today