views:

990

answers:

2

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

alt text

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.

+3  A: 

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.

TechZen
hi TechZen, I have not mentioned that all the relationships are optional except b of A and a of B. Thanks for your suggestions, I will try to debug and report the results.
sfa
sfa
sfa
I don't know immediately, let me think on it. Somethings to try: (*) Try removing E from the graph i.e. break the relationship D<-->E and see if that helps. (*) Instead of starting the delete from B, start from C. (*) After you delete but before you save, call `isDeleted` on all D in each C. See if they are actually all marked for deletion and which C if any they still point to. (*)If the problem occurs with just one D in the entire graph, dump it both before and after the call to delete (but before save) to see if anything critical changes.
TechZen
Are there any instance properties for any of these entities/managedObjects that are not represented in the model? If so, double check them.
TechZen
@TechZen, these are all entities in the model. I have not missed any other entity in the image. Thanks for your help. I'll try again.
sfa
I suggest simplifying the model to just C<->>D and getting that to work, and then adding other entities back in one at a time. These complexdata graphs can be a real explicative-deleted pain to debug.
TechZen
I would add that I don't mind spending the time to help someone who values my time enough to spend the THEIR time putting together a good question.
TechZen
Hi TechZen, thanks for accompanying with me, I have tried to recreate the source code of a project that have exactly the same entities as in the image uploaded.
sfa
I found your problem. See my edit.
TechZen
hi TechZen, thank you very much for your edits. As I told you, I created this project just to test if I have any problem with the schema, so naming variable is just for claration purpose (to be the same with the image uploaded). I have a different project in which I could not publish the source, and offcourse, the variable names are different.
sfa
Ohh, @TechZen, you know what, in the sample application that I created, when changing the relationship, there is no error. But in my real application, it still exists. Because in my applicaiton, all the entities are populated into table view respectively, and the problem lies in the NSFetchedResultsController Delegate as Marcus said. I have just double checked the FetchedResultsController Delegate, I accidentally left all of them empty, so when deleting all entities, there are changes notification to the FetchedResultsController. Bug solved!!! Btw, thank you very much for your time ;)
sfa
glad to help you deserved it.
TechZen
+2  A: 

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.

Marcus S. Zarra
hi Marcus, I have tried to recreate the situation in a new and simple project that has the exactly the same entities in the image. There are three helper functions inside to generate the error, I have included a README file as well. The stack trace is from the other application. If you can not test my application, I will try to update the stack trace again.
sfa
@Marcus, thank you very much, I have solved the problem, it lies in the NSFetchedResultsController Delegate methods. You are right. I doublechecked the view controller and found that I accidentally left all of them empty without any implementation. I have solved the bug by deleting all of them! Thank you for sharing :-)
sfa