On my Lion app, I have this data model:
The relationship subitem
I have had the same problem, but only when I tried something different to what I had been doing. I can't see the code for subItem, but I will assume that it has a reverse link to item. Lets call this reveres link, "parentItem", then the easiest solution is this:
Item *item = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:self.managedObjectContext];
item.name = @"FirstItem";
SubItem *subItem = [NSEntityDescription insertNewObjectForEntityForName:@"SubItem" inManagedObjectContext:self.managedObjectContext];
//[item addSubitemsObject:subItem];
subItem.parentItem = item;
The effect is that it makes use of apple's own code and it is simple and clean. In addition, the set is automatically added to, and all observers are updated. No problem.
Instead to making a copy I suggest to use the accessor in NSObject to get access to the NSMutableOrderedSet of the relationships.
- (void)addSubitemsObject:(SubItem *)value {
NSMutableOrderedSet* tempSet = [self mutableOrderedSetValueForKey:@"subitems"];
[tempSet addObject:value];
}
e.g. the Core Data Release Notes for iOS v5.0 refer to this.
In a short test it worked in my application.
I've decided to improve the solution by implementing all the required methods:
static NSString *const kItemsKey = @"<#property#>";
- (void)insertObject:(<#Type#> *)value in<#Property#>AtIndex:(NSUInteger)idx {
NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
[self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
[tmpOrderedSet insertObject:value atIndex:idx];
[self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}
- (void)removeObjectFrom<#Property#>AtIndex:(NSUInteger)idx {
NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
[self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
[tmpOrderedSet removeObjectAtIndex:idx];
[self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
[self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
}
- (void)insert<#Property#>:(NSArray *)values atIndexes:(NSIndexSet *)indexes {
[self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
[tmpOrderedSet insertObjects:values atIndexes:indexes];
[self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}
- (void)remove<#Property#>AtIndexes:(NSIndexSet *)indexes {
[self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
[tmpOrderedSet removeObjectsAtIndexes:indexes];
[self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
[self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
}
- (void)replaceObjectIn<#Property#>AtIndex:(NSUInteger)idx withObject:(<#Type#> *)value {
NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
[self willChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
[tmpOrderedSet replaceObjectAtIndex:idx withObject:value];
[self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
[self didChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
}
- (void)replace<#Property#>AtIndexes:(NSIndexSet *)indexes with<#Property#>:(NSArray *)values {
[self willChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
[tmpOrderedSet replaceObjectsAtIndexes:indexes withObjects:values];
[self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
[self didChange:NSKeyValueChangeReplacement valuesAtIndexes:indexes forKey:kItemsKey];
}
- (void)add<#Property#>Object:(<#Type#> *)value {
NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
NSUInteger idx = [tmpOrderedSet count];
NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
[self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
[tmpOrderedSet addObject:value];
[self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}
- (void)remove<#Property#>Object:(<#Type#> *)value {
NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
NSUInteger idx = [tmpOrderedSet indexOfObject:value];
if (idx != NSNotFound) {
NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
[self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
[tmpOrderedSet removeObject:value];
[self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
[self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
}
}
- (void)add<#Property#>:(NSOrderedSet *)values {
NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
NSUInteger valuesCount = [values count];
NSUInteger objectsCount = [tmpOrderedSet count];
for (NSUInteger i = 0; i < valuesCount; ++i) {
[indexes addIndex:(objectsCount + i)];
}
if (valuesCount > 0) {
[self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
[tmpOrderedSet addObjectsFromArray:[values array]];
[self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
[self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:kItemsKey];
}
}
- (void)remove<#Property#>:(NSOrderedSet *)values {
NSMutableOrderedSet *tmpOrderedSet = [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:kItemsKey]];
NSMutableIndexSet *indexes = [NSMutableIndexSet indexSet];
for (id value in values) {
NSUInteger idx = [tmpOrderedSet indexOfObject:value];
if (idx != NSNotFound) {
[indexes addIndex:idx];
}
}
if ([indexes count] > 0) {
[self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
[tmpOrderedSet removeObjectsAtIndexes:indexes];
[self setPrimitiveValue:tmpOrderedSet forKey:kItemsKey];
[self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:kItemsKey];
}
}
It seems that if you link the parent with the child by setting the parent to the child and not the other way around it works without crashing.
So if you do:
[child setParent:parent]
instead of
[parent setChildObects:child]
It should work, at least it works on iOS 7 and didn't had any problems with the relationship.
Better version of the correct answer in SWIFT
var tempSet = NSMutableOrderedSet()
if parent!.subItems != nil {
tempSet = NSMutableOrderedSet(orderedSet: parent!.subItems!)
}
tempSet.add(newItem)
parent!.subItems = tempSet
I found using the method by LeeIII worked, but on profiling found it was drastically slow. It took 15 seconds to parse 1000 items. Commenting out the code to add the relationship turned 15 seconds into 2 seconds.
My workaround (which is faster but much more ugly) involves creating a temporary mutable array then copying into the ordered set when all the parsing is done. (this is only a performance win if you are going to add many relationships).
@property (nonatomic, retain) NSMutableArray* tempItems;
....
@synthesize tempItems = _tempItems;
....
- (void) addItemsObject:(KDItem *)value
{
if (!_tempItems) {
self.tempItems = [NSMutableArray arrayWithCapacity:500];
}
[_tempItems addObject:value];
}
// Call this when you have added all the relationships
- (void) commitRelationships
{
if (_tempItems) {
self.items = [NSOrderedSet orderedSetWithArray:self.tempItems];
self.tempItems = nil;
}
}
I hope this help someone else!