Exception thrown in NSOrderedSet generated accessors

后端 未结 25 2553
天涯浪人
天涯浪人 2020-11-22 09:07

On my Lion app, I have this data model:

\"enter

The relationship subitem

25条回答
  •  野性不改
    2020-11-22 09:34

    I've tracked the bug. It occurs in willChangeValueForKey:withSetMutation:usingObjects:.

    This call sets off a chain of notifications which may be difficult to track, and of course changes to one responder may have implications for another, which I suspect is why Apple have done nothing.

    However, it is okay in Set and its only the Set operations on an OrderedSet that malfunction. That means there are only four methods that need to be altered. Therefore, all I did was convert the Set operations to their equivalent Array operations. These work perfectly and minimal (but necessary) overheads.

    On a critical level, this solution does suffer from one critical flaw; if you are adding objects and one of the objects already exists, then it is either not added or moved to the back of the ordered list (I don't know which). In either case, the expected ordered index of the object by the time we arrive at didChange is different from what was anticipated. This may break some people's apps, but it doesn't affect mine, since I am only ever adding new objects or I confirm their final locations before I add them.

    - (void)addChildrenObject:(BAFinancialItem *)value {
        if ([self.children containsObject:value]) {
            return;
        }
        NSIndexSet * indexSet = [NSIndexSet indexSetWithIndex:self.children.count];
        [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
        [[self primitiveValueForKey:ChildrenKey] addObject:value];
        [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
    }
    
    - (void)removeChildrenObject:(BAFinancialItem *)value {
        if (![self.children containsObject:value]) {
            return;
        }
        NSIndexSet * indexSet = [NSIndexSet indexSetWithIndex:[self.children indexOfObject:value]];
        [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
        [[self primitiveValueForKey:ChildrenKey] removeObject:value];
        [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
    }
    
    - (void)addChildren:(NSOrderedSet *)values {
        if ([values isSubsetOfOrderedSet:self.children]) {
            return;
        }
        NSIndexSet * indexSet = [NSIndexSet indexSetWithIndexesInRange:NSMakeRange(self.children.count, values.count)];
        [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
        [[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values];
        [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexSet forKey:ChildrenKey];
    }
    
    - (void)removeChildren:(NSOrderedSet *)values {
        if (![self.children intersectsOrderedSet:values]) {
            return;
        }
        NSIndexSet * indexSet = [self.children indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
            return [values containsObject:obj];
        }];
        [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
        [[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values];
        [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexSet forKey:ChildrenKey];
    }
    

    Of course, there is an easier solution. it is as follows;

    - (void)addChildrenObject:(BAFinancialItem *)value {
        if ([self.children containsObject:value]) {
            return;
        }
        [self insertObject:value inChildrenAtIndex:self.children.count];
    }
    
    - (void)removeChildrenObject:(BAFinancialItem *)value {
        if (![self.children containsObject:value]) {
            return;
        }
        [self removeObjectFromChildrenAtIndex:[self.children indexOfObject:value]];
    }
    
    - (void)addChildren:(NSOrderedSet *)values {
        if ([values isSubsetOfOrderedSet:self.children]) {
            return;
        }
        [self insertChildren:values atIndexes:[NSIndexSet indexSetWithIndexesInRange:NSMakeRange(self.children.count, values.count)]];
    }
    
    - (void)removeChildren:(NSOrderedSet *)values {
        if (![self.children intersectsOrderedSet:values]) {
            return;
        }
        [self removeChildrenAtIndexes:[self.children indexesOfObjectsPassingTest:^BOOL(id obj, NSUInteger idx, BOOL *stop) {
            return [values containsObject:obj];
        }]];
    }
    

提交回复
热议问题