Question

I have the following model, as you can see in the image.

alt text http://img521.imageshack.us/img521/9741/schermata20100224a12251.png

My application requires refreshing every instance of B, so at each viewWillAppear, I need to delete all the B's in the model. Upon deleting a B, the cascade delete rule on the relationship to C will delete all C and then cascade to all D. A & E are constants.

I have the DeleteRule on each object as follows:

A: b - Cascade
B: c - Cascade, a - Nullify
C: b - Nullify, d - Cascade
D: c - Nullify, e - Nullify
E: d - Cascade

or

A -(cascade)->> B -(cascade)-> C -(cascade)->> D -(nullify)-> E
A <-(nullify)- B <-(nullify)- C <-(nullify)- D <-(nullify) E

I am having problem with cascading the delete on all B,C,D. My fetchRequest object returns every instance of B in A and then I call the -deleteObject: on each B from the managedObjectContext. But there is the EXC_BAD_ACCESS on the call to [managedObjectContext save:&error].

Can someone show me what I am doing wrong? Am I having problems with the DeleteRule on each entity or does the problem lay elsewhere? What are the best practices to handle the cascading deletes on three objects B,C,D?

Edited:

Here is the stack trace when the error is raised:

  #0    0x01d843ae in ___forwarding___
  #1    0x01d606c2 in __forwarding_prep_0___
  #2    0x01c618b6 in -[NSFetchedResultsController(PrivateMethods) _managedObjectContextDidChange:]
  #3    0x0003263a in _nsnote_callback
  #4    0x01d4f005 in _CFXNotificationPostNotification
  #5    0x0002fef0 in -[NSNotificationCenter postNotificationName:object:userInfo:]
  #6    0x01bc217d in -[NSManagedObjectContext(_NSInternalNotificationHandling) _postObjectsDidChangeNotificationWithUserInfo:]
  #7    0x01c21763 in -[NSManagedObjectContext(_NSInternalChangeProcessing) _createAndPostChangeNotification:withDeletions:withUpdates:withRefreshes:]
  #8    0x01ba65ea in -[NSManagedObjectContext(_NSInternalChangeProcessing) _processRecentChanges:]
  #9    0x01bdc728 in -[NSManagedObjectContext save:]

Here is the log in the console when I tried to set NSZombieEnabled & MallocStackLogging to YES:

  2010-02-24 15:41:39.803 Foo[2591:207] deleting object: FUM5
  2010-02-24 15:41:40.515 Foo[2591:207] *** -[viewController controllerWillChangeContent:]: message sent to deallocated instance 0x7e54510

Edit 2: SOURCE CODE ADDED

I have tried to recreate the situation by creating a new project with the exact schema in the image. You can download it from here. There is a README text as well. Hope that I have given enough information.

Was it helpful?

Solution

What are you doing in your NSFetchedResultsControllerDelegate methods? Based on the stack trace it looks like you are doing something funny in one of those. Ideally those delegates should only be updating the UITableView that they are attached to. If you are doing something either with the NSManagedObject instances or the NSManagedObjectContext in one of those methods it can cause a crash like this.

I would suggest putting a breakpoint on objc_exception_throw and that can give you more information about the exact point the issue is occurring.

Update

I have looked at the code and you are trying to delete a B while A has a required property for that B. This is causing a validation error. You can't delete an object that another object has as a required relationship.

OTHER TIPS

I believe the problem might lay in a required relationship between C and D. If you have the graph configured such that:

  1. Each C requires at least one D.
  2. Many instances of C point to the same D.

Then at some point a C is likely to find that it's required relationship has been nullified. If it tries to access the D anyway, that will trigger the EXC_BAD_ACCESS. (If each E requires one D you might get the same problem)

To debug I suggest,

  • Set the relationship C-->D as optional and see if the error goes away.
  • Change the cascades to nulls one at time starting with C-->D and see if the error goes away.
  • If you have classes, check that the instances don't share some object in common outside the entity graph. For example, both C and D classes have a referenced to the same image but that image is not part of the entity. If the external references is not properly retained that can also cause a similar crash.
  • Move your save to immediately after the call to delete each B. Log each B and it's Cs before you delete. That way you can see exactly were the save fails and what state the graph is in when it does.

Edit01:

Okay, I looked at your code and found the problem.

The problem is that you have the "b" relationship of A set to required. When you delete B it throws this error:

2010-02-24 16:14:02.064 CoreDataTestDeleteRule[20887:207] Unresolved error Error Domain=NSCocoaErrorDomain Code=1580 UserInfo=0x3d0b450 "Operation could not be completed. (Cocoa error 1580.)"
2010-02-24 16:14:06.340 CoreDataTestDeleteRule[20887:207] Unresolved error Error Domain=NSCocoaErrorDomain Code=1580 UserInfo=0x3d19980 "Operation could not be completed. (Cocoa error 1580.)", {
    NSLocalizedDescription = "Operation could not be completed. (Cocoa error 1580.)";
    NSValidationErrorKey = b;
    NSValidationErrorObject = <A: 0x3b2faf0> (entity: A; id: 0x3d05330 <x-coredata://6870AF7C-E28F-4B4E-80AB-09C648651179/A/p1> ; data: {
    b =     (
    );
    name = a;

(BTW, The text errors for the numerical Core Data error codes can be found in CoreDataErrors.h.)

This makes sense because you require A to have a b and then you delete all b's. Simply setting the A->>B relationship to optional prevents the error and allows the code to run fine.

You code also has some other problems. Some of the auto generated classes didn't turn out correctly. For example, the interface for B.h looks like this:

#import <CoreData/CoreData.h>

@class A;

@interface B :  NSManagedObject  
{
}

@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) A * a;
@property (nonatomic, retain) NSManagedObject * c;

@end

When it should look like:

#import <CoreData/CoreData.h>

@class A;
@class C;

@interface B :  NSManagedObject  
{
}

@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) A * a;
@property (nonatomic, retain) C * c;

@end

I'm not sure why they didn't turn generate correctly.

I would also advise against using short variable and class names. Objective-c has a global name space and using single letter symbols is an invitation for a name collision. You never know who else was in a hurry. I recommending using the old WWII style phonetic alphabet (the modern one also risk collisions) and name test classes: Adam, Baker, Charlie, David, Eddy etc.

It looks like we both got focused on the B<->C<->>D relationship and neglected to look farther afield. It's the programmer's debugging version of the military's "target fixation". You get stuck on one concept of the problem and can't shake yourself loose.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top