简体   繁体   中英

How to trigger notifications when item added to or removed from Core Data NSManagedObject relationship's NSSet?

What is the correct way to trigger an NSNotification whenever an item is added to or removed from a to-many relationship / NSSet within a custom NSManagedObject subclass?

I have a custom NSManagedObject subclass that has a one-to-many unordered relationship to another NSManagedObject subclass. For clarity, let's say these two subclasses are Teacher and Student , where one Teacher can have multiple Student objects but where each Student is only assigned to one Teacher .

I'd like to be able to trigger a notification whenever a Student is added to or removed from a Teacher , whether because Student was simply assigned to or from a Teacher or whether because Student was deleted entirely from Core Data.

I tried using KVO but it doesn't seem like you can add an observer to an NSSet 's count property add an observer to a @dynamic property. Additionally, I tried implementing my own custom to-many accessor method as outlined in Apple's documentation , but in testing it seems like my custom accessor methods are never called. In case there is something wrong with my implementation, here is how I implemented it within Teacher :

@implementation Teacher

@dynamic students;

- (void)addStudentsObject:(Student *)value
{
    NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
    [self willChangeValueForKey:NSStringFromSelector(@selector(students))
                withSetMutation:NSKeyValueUnionSetMutation
                   usingObjects:changedObjects];
    [[self primitiveStudents] addObject:value];
    [self didChangeValueForKey:NSStringFromSelector(@selector(students))
               withSetMutation:NSKeyValueUnionSetMutation
                  usingObjects:changedObjects];
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_TEACHER_STUDENT_WAS_ADDED object:self];
}

- (void)removeStudentsObject:(Student *)value
{
    NSSet *changedObjects = [[NSSet alloc] initWithObjects:&value count:1];
    [self willChangeValueForKey:NSStringFromSelector(@selector(students))
                withSetMutation:NSKeyValueMinusSetMutation
                   usingObjects:changedObjects];
    [[self primitiveStudents] removeObject:value];
    [self didChangeValueForKey:NSStringFromSelector(@selector(students))
               withSetMutation:NSKeyValueMinusSetMutation
                  usingObjects:changedObjects];
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_TEACHER_STUDENT_WAS_REMOVED object:self];
}

- (void)addStudents:(NSSet *)values
{
    [self willChangeValueForKey:NSStringFromSelector(@selector(students))
                withSetMutation:NSKeyValueUnionSetMutation
                   usingObjects:values];
    [[self primitiveStudents] unionSet:values];
    [self didChangeValueForKey:NSStringFromSelector(@selector(students))
               withSetMutation:NSKeyValueUnionSetMutation
                  usingObjects:values];
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_TEACHER_STUDENTS_WERE_ADDED object:self];
}

- (void)removeStudents:(NSSet *)values
{
    [self willChangeValueForKey:NSStringFromSelector(@selector(students))
                withSetMutation:NSKeyValueMinusSetMutation
                   usingObjects:values];
    [[self primitiveStudents] minusSet:values];
    [self didChangeValueForKey:NSStringFromSelector(@selector(students))
               withSetMutation:NSKeyValueMinusSetMutation
                  usingObjects:values];
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_TEACHER_STUDENTS_WERE_REMOVED object:self];
}

...

@end

The fundamental issue turns out to be that the fancy accessor examples in the Apple Docs that you were using as a reference are actually not all the accessors for the relationship. They are extra convenience accessors that you can implement if you want. But because you are trying to insert your code by overriding all the setters (or whatever you want to call mutating accessors), you need to also override the most basic setter.

The most basic accessors for a to-many relationship are setters and getters for the NSSet object for the entire relationship. In your case, - (NSSet*) students and - (void) setStudents:(NSSet*)students .

So, at a minimum, your notification needs to be posted in setStudents :

-(void) setStudents:(NSSet*)students{ 
    [self willChangeValueForKey:@"students"]; 
    [self setPrimitiveValue:students forKey:@"students"]; 
    [self didChangeValueForKey:@"students"]; 
    [[NSNotificationCenter defaultCenter] postNotificationName:NOTIFICATION_TEACHER_STUDENTS_WERE_ADDED object:self];

} 

That is the setter being used by default.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM