edelaney05
edelaney05

Reputation: 6997

Core Data relationship delete rule - Nullify + Cascade

Entities:

Relationships:

In the Playlist entity, I have not exposed the playlistItem relationship / set. Instead I have a transient undefined attribute called "videos," which is an array of Video entities generated by sorting the set of Playlist Items. There is not a relationship between Playlist and Video entities.

I'm having problems when saving a modified Playlist instance in the Managed Object Context.

From playlist.m (NSManagedObject subclass):

- (BOOL)validatePlaylistItems:(NSSet **)playlistItems error:(NSError **)outError
{
    NSArray *currentVideos = [self videos];
    NSArray *persistedVideos = [self videosFromPlaylistItems];

    if ([currentVideos isEqual:persistedVideos]) {
        return YES;
    }

    NSManagedObjectContext *context = [self managedObjectContext];
    for (FHPlaylistItem *pi in *playlistItems) {
        [context deleteObject:pi];
    }

    NSArray *videos = [self primitiveVideos];
    NSUInteger count = [videos count];
    for (int i = 0; i < count; i++) {
        FHPlaylistItem *pi = [FHPlaylistItem playlistItemWithVideo:[videos objectAtIndex:i]
                                                          forIndex:i
                                    insertIntoManagedObjectContext:[self managedObjectContext]];
        [self addPlaylistItemsObject:pi];
        [pi setPlaylist:self];
    }

    return YES;
}

What I'm trying to accomplish here is only update the set of Playlist Items when the MOC needs to save, as you can see the operation is expensive - O(N * 2). During the first run of the app, the MOC saves just fine. However, subsequent changes to the videos attribute yields in Core Data spewing a bunch of errors:

Core Data: annotation: repairing missing delete propagation for to-many relationship playlistItems on object <FHPlaylist: 0x6c27eb0> (entity: Playlist; id: 0x6c26d40 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/Playlist/p19> ; data: {
    accountID = 0;
    lastFetched = "2012-03-31 20:05:08 +0000";
    name = Featured;
    playlistID = 1441335931001;
    playlistItems =     (
        "0x6c34be0 <x-coredata:///PlaylistItem/t9EAFBC87-5E16-4053-984F-881CCD9C1F0B2>",
        "0x6c34320 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/PlaylistItem/p16>",
        "0x6c310c0 <x-coredata:///PlaylistItem/t9EAFBC87-5E16-4053-984F-881CCD9C1F0B3>",
        "0x6c35570 <x-coredata:///PlaylistItem/t9EAFBC87-5E16-4053-984F-881CCD9C1F0B5>",
        "0x6c34ca0 <x-coredata:///PlaylistItem/t9EAFBC87-5E16-4053-984F-881CCD9C1F0B6>",
        "0x6c34310 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/PlaylistItem/p8>",
        "0x6c28a90 <x-coredata:///PlaylistItem/t9EAFBC87-5E16-4053-984F-881CCD9C1F0B4>",
        "0x6c34350 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/PlaylistItem/p29>",
        "0x6c34220 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/PlaylistItem/p2>",
        "0x6c34330 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/PlaylistItem/p20>",
        "(...and 1 more...)"
    );
    playlistType = 0;
    referenceID = OKFFeaturedPlaylist;
    shortDescrip = "This is so descriptive! OMG!";
    thumbnailURL = nil;
    videos = "(...not nil..)";
}) with bad fault 0x6c34320 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/PlaylistItem/p16>
Core Data: annotation: repairing missing delete propagation for to-many relationship playlistItems on object <FHPlaylist: 0x6c27eb0> (entity: Playlist; id: 0x6c26d40 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/Playlist/p19> ; data: {
    accountID = 0;
    lastFetched = "2012-03-31 20:05:08 +0000";
    name = Featured;
    playlistID = 1441335931001;
    playlistItems =     (
        "0x6c34be0 <x-coredata:///PlaylistItem/t9EAFBC87-5E16-4053-984F-881CCD9C1F0B2>",
        "0x6c310c0 <x-coredata:///PlaylistItem/t9EAFBC87-5E16-4053-984F-881CCD9C1F0B3>",
        "0x6c35570 <x-coredata:///PlaylistItem/t9EAFBC87-5E16-4053-984F-881CCD9C1F0B5>",
        "0x6c34ca0 <x-coredata:///PlaylistItem/t9EAFBC87-5E16-4053-984F-881CCD9C1F0B6>",
        "0x6c34310 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/PlaylistItem/p8>",
        "0x6c28a90 <x-coredata:///PlaylistItem/t9EAFBC87-5E16-4053-984F-881CCD9C1F0B4>",
        "0x6c34350 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/PlaylistItem/p29>",
        "0x6c34220 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/PlaylistItem/p2>",
        "0x6c34330 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/PlaylistItem/p20>",
        "0x6c34340 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/PlaylistItem/p27>"
    );
    playlistType = 0;
    referenceID = OKFFeaturedPlaylist;
    shortDescrip = "This is so descriptive! OMG!";
    thumbnailURL = nil;
    videos = "(...not nil..)";
}) with bad fault 0x6c34310 <x-coredata://BCB69D8E-8393-4A2A-AF5D-0AA1872CE2B4/PlaylistItem/p8>
Etc. Etc. Etc.

It seems to be the problem lies with the delete rule. The complaint is with a "missing delete propagation," so it seems nullify is the wrong choice. But, why is it wrong? This doesn't make sense to me. The wording of the cascade delete rule in Apple's Core Data Programming Guide makes it seem like I'm going to delete the Video and Playlist objects if I set Playlist Item's delete rule to cascade.

Cascade - Delete the objects at the destination of the relationship

Furthermore, if I set the relationship from Playlist Item to video & playlist to nil in the fast enumeration loop, Core Data doesn't complain about having to repair the missing delete propagation.

for (FHPlaylistItem *pi in *playlistItems) {
    [pi setVideo:nil];
    [pi setPlaylist:nil];
    [context deleteObject:pi];
}

EDIT: Nope, nil'ing the relationships manually didn't work in all situations. (Maybe no surprise?)

Upvotes: 5

Views: 6513

Answers (4)

Ali Saeed
Ali Saeed

Reputation: 1569

using performBlock fixed it for me:

[context performBlock:^{

// delete objects

// save context

}];

Upvotes: 0

alexantd
alexantd

Reputation: 3605

-processPendingChanges didn't work for me. What did work was to implement -prepareForDeletion in my NSManagedObject subclass and manually remove the object-to-be-deleted from the problematic to-many relationship:

- (void)prepareForDeletion {
    [[[self owningEntity] mutableSetValueForKey:@"ownedEntities"] removeObject:self];
}

I've triple-checked my data model and it looks OK. I don't know why this error is being produced. But this seems to make it disappear, at least.

Upvotes: 1

svena
svena

Reputation: 2779

Try if [[self managedObjectContext] processPendingChanges] after your delete block will solve the strange messages for you.

It seems Core Data will get confused in some situations when you go on altering the managed object context in the same run loop cycle and some of the involved objects had relationships to the deleted objects.

Regards,

sven.

Upvotes: 10

Wienke
Wienke

Reputation: 3733

On the Playlist entity, I have not exposed the playlistItem relationship / set. Instead I have a transient undefined attribute called "videos," which is an array of video generated by sorting the set of Playlist Items.

Maybe that's the problem. It's not clear how you've named the relationship to Video. If both the relationship and the transient attribute are named videos you would definitely have a problem. Why not declare the videos relationship normally in Playlist's h file, in addition to (rather than instead of) your declaration for, say, videosSorted (the transient array).

Also, make sure it's clear in the model which relationships are the inverse of each other.

Upvotes: 0

Related Questions