Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Core Data relationship delete rule - Nullify + Cascade

Tags:

core-data

ios5

Entities:

  • Video
  • Playlist Item
  • Playlist

Relationships:

  • Video is optional to-many to Playlist Item and the delete rule is nullify
  • Playlist is to-many to at least one Playlist Items and the delete rule is nullify
  • Playlist Item is required to-one for Video and the delete rule is nullify
  • Playlist Item is required to-one for Playlist and the delete rule is nullify

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?)

like image 990
edelaney05 Avatar asked Apr 01 '12 19:04

edelaney05


People also ask

What is relationship in Core Data?

Inverse relationships enable Core Data to propagate change in both directions when an instance of either the source or destination type changes. Every relationship must have an inverse. When creating relationships in the Graph editor, you add inverse relationships between entities in a single step.

What is the function of a deletion rule?

Setting the Delete Rule to Protect prevents deleting records of the main Entity while there are associated records in the related Entity. This behavior is ensured by a database constraint created on the reference attribute.

What is Core Data stack in Swift?

Overview. Core Data provides a set of classes that collaboratively support your app's model layer: An instance of NSManagedObjectModel describes your app's types, including their properties and relationships. An instance of NSManagedObjectContext tracks changes to instances of your app's types.


1 Answers

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.

like image 194
svena Avatar answered Nov 14 '22 21:11

svena