[cocoa] NSOrderedSet에서 생성 된 접근 자에서 예외가 발생했습니다.

Lion 앱에는 다음과 같은 데이터 모델이 있습니다.

여기에 이미지 설명을 입력하십시오

subitems내부 관계 Item 가 주문 됩니다.

엑스 코드 4.1 (빌드 4B110)는 나를 위해 파일을 만들었습니다 Item.h, Item.m, SubItem.hSubItem.h.

다음은 (자동 생성 된) 내용입니다 Item.h.

#import <Foundation/Foundation.h>

#import <CoreData/CoreData.h>

@class SubItem;

@interface Item : NSManagedObject {
@private
}

@property (nonatomic, retain) NSString * name;
@property (nonatomic, retain) NSOrderedSet *subitems;
@end

@interface Item (CoreDataGeneratedAccessors)

- (void)insertObject:(SubItem *)value inSubitemsAtIndex:(NSUInteger)idx;
- (void)removeObjectFromSubitemsAtIndex:(NSUInteger)idx;
- (void)insertSubitems:(NSArray *)value atIndexes:(NSIndexSet *)indexes;
- (void)removeSubitemsAtIndexes:(NSIndexSet *)indexes;
- (void)replaceObjectInSubitemsAtIndex:(NSUInteger)idx withObject:(SubItem *)value;
- (void)replaceSubitemsAtIndexes:(NSIndexSet *)indexes withSubitems:(NSArray *)values;
- (void)addSubitemsObject:(SubItem *)value;
- (void)removeSubitemsObject:(SubItem *)value;
- (void)addSubitems:(NSOrderedSet *)values;
- (void)removeSubitems:(NSOrderedSet *)values;

@end

그리고 다음은 (자동 생성 된) 내용입니다 Item.m:

#import "Item.h"
#import "SubItem.h"

@implementation Item

@dynamic name;
@dynamic subitems;

@end

보다시피이 클래스 Item는라는 메소드를 제공합니다 addSubitemsObject:. 불행히도, 이런 식으로 사용하려고 할 때 :

Item *item = [NSEntityDescription insertNewObjectForEntityForName:@"Item" inManagedObjectContext:self.managedObjectContext];
item.name = @"FirstItem";

SubItem *subItem = [NSEntityDescription insertNewObjectForEntityForName:@"SubItem" inManagedObjectContext:self.managedObjectContext];

[item addSubitemsObject:subItem];

이 오류가 나타납니다.

2011-09-12 10:28:45.236 Test[2002:707] *** -[NSSet intersectsSet:]: set argument is not an NSSet

도와주세요?

최신 정보:

Apple은 오늘 (2016 년 8 월 1 일) 내 버그 보고서에서 1,787 일 만에 다음과 같이 썼습니다. “최신 iOS 10 베타 빌드로이 문제를 확인하고 bugreport.apple.com에서 버그 보고서를 결과로 업데이트하십시오.” . 이것이 올바른 시간이 되길 바랍니다 🙂



답변

데이터 모델과 다른 이름을 가진 자체 모델 중 하나를 사용하여 설정을 재현했습니다. 두 경우 모두 같은 오류가 발생했습니다.

Apple의 자동 생성 코드의 버그처럼 보입니다.


답변

여기에 버그가있을 수 있음에 동의합니다. NSMutableOrderedSet에 올바르게 추가하기 위해 add object setter의 구현을 수정했습니다.

- (void)addSubitemsObject:(SubItem *)value {
    NSMutableOrderedSet* tempSet = [NSMutableOrderedSet orderedSetWithOrderedSet:self.subitems];
    [tempSet addObject:value];
    self.subitems = tempSet;
}

집합을 self.subitems로 다시 할당하면 Will / DidChangeValue 알림이 전송됩니다.


답변

필요한 모든 방법을 구현하여 솔루션을 개선하기로 결정했습니다.

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];
    }
}


답변

예, 이것은 분명히 핵심 데이터 버그입니다. 나는 ObjC- 런타임 기반 수정을 얼마 전에 작성했지만 그 시점에서 곧 수정 될 것이라고 생각했습니다. 어쨌든 그런 운이 없기 때문에 GitHub에 KCOrderedAccessorFix 로 게시했습니다 . 모든 엔티티에 대한 문제를 해결하십시오.

[managedObjectModel kc_generateOrderedSetAccessors];

특히 하나의 실체 :

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

아니면 하나의 관계 만 :

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];


답변

복사하는 대신 NSObject의 접근자를 사용하여 관계의 NSMutableOrderedSet에 액세스하는 것이 좋습니다.

- (void)addSubitemsObject:(SubItem *)value {
      NSMutableOrderedSet* tempSet = [self mutableOrderedSetValueForKey:@"subitems"];
      [tempSet addObject:value];
 }

예를 들어 iOS v5.0 용 Core Data Release Notes 가이를 참조합니다.

짧은 테스트에서 그것은 내 응용 프로그램에서 작동했습니다.


답변

버그를 추적했습니다. 에서 발생합니다 willChangeValueForKey:withSetMutation:usingObjects:.

이 전화는 추적하기 어려울 수있는 일련의 알림을 설정하고 물론 한 응답자에 대한 변경 사항이 다른 응답자에게 영향을 줄 수 있으므로 Apple이 아무런 조치를 취하지 않은 것 같습니다.

그러나 Set에서는 문제가없고 OrderedSet에 대한 Set 작업 만 가능합니다. 즉, 변경해야 할 네 가지 방법 만 있습니다. 따라서 Set 작업을 동등한 Array 작업으로 변환하기 만했습니다. 이것들은 완벽하고 최소한의 (필요한) 오버 헤드로 작동합니다.

중요한 수준에서이 솔루션은 하나의 중요한 결함으로 인해 어려움을 겪습니다. 객체를 추가하고 있고 객체 중 하나가 이미 존재하는 경우 객체가 추가되거나 순서가 지정된 목록의 뒷면으로 이동되지 않습니다 (어떤 것을 모르겠습니다). 두 경우 모두, 우리가 도착할 때까지 개체의 예상 순서 인덱스 didChange는 예상과 다릅니다. 이것은 일부 사람들의 앱을 망가뜨릴 수 있지만 새로운 객체 만 추가하거나 추가하기 전에 최종 위치를 확인하기 때문에 영향을 미치지 않습니다.

- (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];
}

물론 더 쉬운 해결책이 있습니다. 다음과 같습니다.

- (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];
    }]];
}


답변

많은 관계로 애플 문서는 말한다 : 프록시 변경 가능한 설정에 액세스해야하거나 사용하여 설정 순서화

NSMutableOrderedSet * set = [managedObject mutableOrderedSetValueForKey:@"toManyRelation"];

이 세트를 수정하면 관리 대상 객체에 관계가 추가되거나 제거됩니다. [] 또는을 사용하여 액세서를 사용하여 변경 가능 순서 ​​세트에 액세스합니다. 표기법이 잘못되어 실패합니다.