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


364

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에서 버그 보고서를 결과로 업데이트하십시오." . 이것이 올바른 시간이 되길 바랍니다 :)


5
같은 문제가 발생합니다. 잘만되면 그것은 곧 고쳐질 것이다. 변경 가능한 주문 세트를 직접 사용하는 것은 당분간 쉬운 해결 방법입니다. 참고 : mogenerator를 사용하고 있지만 생성 된 코드 의이 부분에 대해 동일한 Apple 생성기를 내부적으로 사용한다고 가정합니다.
Chad Podoski

12
거의 2 년입니다! iOS 7, Apple에서 수정 하시겠습니까? ——이 버그가 아직 있는지 궁금한 사람들과 공유하고 싶습니다. "예, 그래요."
an0

1
2 년 가까이되었지만 여전히 모든 xcode 5 개발자 미리보기에서 문제가됩니다.
Korvin Szanto

2
적절한 KVC 접근자를 사용하면 여전히 문제가 발생합니까? (예 mutableOrderedSetValueForKey:)
quellish

3
Mavericks에 여전히 문제가있는 것으로 보입니다.

답변:


263

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

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


60
버그 ID는 10114310입니다. 2011 년 9 월 13 일에보고되었지만 현재 (2012 년 1 월 15 일) 여전히 "열렸습니다". 같은 문제를 가진 사람들의 수를 고려하면 놀랍습니다.
Dev

14
업데이트 : 오늘 (2012 년 5 월 11 일) 버그 # 10114310은 내 보고서 (2011 년 9 월 13 일) 후 241 일 후에 여전히 열려 있습니다. 믿을 수 없는.
Dev

23
WWDC에서 CoreData Lab 세션 중 하나를 Apple 엔지니어와 함께 진행했습니다. 그들은 문제를 인정하고 그것이 진짜 버그이며 내가 본 것에서 "치명적인"상태를 가지고 있지만, 그들이 언제 고칠 것인지에 대한 약속은 없습니다. iOS6 / Mountain Lion에서 수정 될 것이라고 생각하지 않습니다. 이 레이더를 더 복제하는 것이 좋을 것 같습니다. 현재 약 25 개의 dup을 가지고 있으며 더 좋습니다.
DaGaMs

40
오늘 방금 확인했는데 여전히 iOS 7 GM / OMG에 있습니다! 믿을 수 없어…
an0

79
업데이트 : 버그 # 10114310을 채운 후 797 일, 2 개의 새로운 주요 iOS 버전 및 수많은 Xcode 버전이 통과했습니다. 그리고 여전히 "개방적"입니다. 믿을 수 없는.
Dev

244

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

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

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


코드 스 니펫은이 문제를 해결하는 데 필요한 것입니다. Apple이 결국 문제를 해결하기를 바랍니다.하지만 지금까지는 귀하의 접근 방식과 관련된 문제를 보지 못했습니다.
Christopher Hujanen

이 해결 방법을 구현하려고 할 때이 오류가 발생합니다. [__NSArrayI isEqualToSet :] : 인식 할 수없는 선택자가 인스턴스로 전송되었습니다. 이것은 일반적으로 릴리스 된 항목에서 왔지만 어느 곳에서나 실행할 수 없습니다. 이것으로?
DerekH

@DerekH isEqualToSet은 NSSet 만있는 메소드이므로 NSManagedObject로 다시 전달하기 전에 포인터를 NSArray로 변환하거나 만들거나 포인터를 처리하는 것 같습니다. 어떤 이유로 든 isEqualToOrderedSet을 호출하여 집합이 필요한지 확인해야합니다. 그대로 변경하거나 남겨 두는 것입니다.
InitJason

3
@MarkAmery 테스트되었습니다. 확인되었습니다. 동적 setter self.subitems는 알림을 보냅니다. 따라서 JLust 솔루션이 정확합니다.
번스타인

3
이것은 좋은 대답이지만 비효율적입니다. 주문한 전체 세트를 복사하고 수정 한 다음 다시 복사합니다. 효과는 단지 주문 된 세트에 대한 히트 일뿐만 아니라, 주문 된 세트가 변경 될 때마다 전체 내용이 변경되었다는 알림이 전송됩니다! 예를 들어이 순서 집합이 UITable에 사용되는 경우 업데이트에 심각한 영향을 줄 수 있습니다. 내 솔루션에서 오류의 정확한 위치를 설명했으며 오류를 무시하는보다 효율적인 방법을 보여주었습니다.
Owen Godfrey

111

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

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

1
충돌 유형은 무엇입니까? 'removeObjectFromSubitemsAtIndex'는 이러한 하위 항목을 삭제하지 않고 저장 영역에 여전히 존재하며 이는 오브젝트 간의 관계를 제거하는 방법 일뿐입니다.
Dmitry Makarenko

2
kItemsKey는 KVO 메소드 호출에서 편의를 위해 추가 된 상수입니다. 그것은 당신이 당신의 방법을 쓰는 질서 관계의 이름입니다.
Dmitry Makarenko

1
그것이 내가 생각하는 것입니다. 감사. 그러나 내 문제는 이러한 방법을 사용하여 데이터가 데이터베이스에 저장되지 않는다는 것입니다.
Bagusflyer

4
!!!!!!!!! 코드를 복사하고 메소드 이름을 변경하면 완벽하게 작동합니다 !!! 이것이 가장 빠른 답변입니다.
flypig

1
이것은 훌륭하지만 주문 된 세트의 임시 사본을 작성할 필요는 없습니다. 범인은 willChangeValueForKey:withSetMutation:usingObjects입니다. 성공적으로 피했습니다. 그 후에는 [[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values]또는 [[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values]적절하게 사용하십시오. 자세한 내용은 내 답변을 참조하십시오.
Owen Godfrey

38

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

[managedObjectModel kc_generateOrderedSetAccessors];

특히 하나의 실체 :

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

아니면 하나의 관계 만 :

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];

이것이 Apple의 실제 수정 사항과 충돌하는지 궁금합니다.
tia

3
목적에 관계없이 Apple의 구현을 재정의하는 것이 목적이므로 Apple의 수정과 충돌해서는 안됩니다. Apple에서 실제로 수정 한 경우 - (BOOL)kc_needsOrderedSetAccessorFix;Foundation / iOS 버전을 확인 하는 항목을 추가 할 것입니다.
스털링 아처

2
CocoaPods 마스터 리포지토리에 KCOrderedAccessorFix.podspec이 이미 있습니다. 이를 프로젝트에 연결하기 위해 간단히 "pod 'KCOrderedAccessorFix'"를 Podfile에 추가하면됩니다.
Anton Matosov

이것은 아이폰 OS와 8 (잘못된 방법 서명 몇 가지 문제가 있었다 objc_msg_send)
NSTJ을

iOS9에서는 잘 작동합니다! 이것은 최고의 솔루션이므로 코드에서 아무것도 변경할 필요가 없습니다!
Borzh December

32

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

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

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

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


1
리터럴 문자열을 쉽게 리팩토링 할 수 없습니다. 코드를 사용하는 경우 컴파일러는 check self.subitems를 입력 할 수 있습니다.
logancautrell

1
@logancautrell 예 맞습니다. 특정 사용 사례의 우선 순위에 따라 다릅니다. 일반적으로이 경우에는 리소스를 절약하는 데 중점을 두었습니다. 이는 해결 방법 일뿐입니다.
Stephan

2
리터럴 문자열은 다음과 같이 대체 될 수 있습니다 NSStringFromSelector(@selector(subitems)):)
Ja͢ck

17

버그를 추적했습니다. 에서 발생합니다 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];
    }]];
}

다른 사람들이 모두이 대답을 간과 한 것 같습니다. 최고의 솔루션처럼 보입니다.
George

이 솔루션은 orderedSetWithOrderedSet을 사용하여 로컬 세트를 만드는 것보다 훨씬 나은 성능을 제공합니다. 큰 데이터 세트가있는 경우 큰 오버 헤드가 발생합니다. 더 쉬운 솔루션은 표시되지 않은 방법으로 초기 버전의 리팩토링 된 버전 인 것처럼 보입니다.
David Pettigrew

1
addChildren에서 여전히 충돌이 발생합니다. *** 잡히지 않은 예외 'NSInvalidArgumentException'으로 인해 앱을 종료하는 이유 : '-[TrackHistory insertTrackpoints : atIndexes :] : 인식 할 수없는 선택자가 인스턴스 0x1702b1b20에 전송 됨
Victor Bogdan

@OwenGodfrey보다 쉬운 솔루션을 위해 이러한 방법을 어디에 구현하고 있습니까? 예외가 발생했습니다. [Parent insertObject : inChildrenAtIndex :] 인식 할 수없는 선택기가 인스턴스 0x6180000ac480으로 전송되었습니다.
Dalmazio

변수가 "P"가있는 "부모"입니까? 클래스를 "Parent"라고 부르거나 "Parent"라는 인스턴스 변수가 있습니까? 내 클래스가 Parent 인 경우 Parent의 맨 아래 에이 메소드를 구현했지만 인스턴스에서 호출해야합니다. 클래스 메소드가 아니기 때문에 소문자 "p"로 "parent"로 명명 될 가능성이 높습니다. .
Owen Godfrey

10

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

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

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


3
공정하게 말하면 문서는 또한 "또는 자동 생성 된 관계 뮤 테이터 방법 중 하나 (동적으로 생성 된 접근 자 방법 참조)"라고 말합니다.
Matt

알았어 .. 네 말이 맞아 그럼, 그 ... 간단한 작업 방법 가정 해 봅시다
니콜라스 만지니

9

@LeeIII 솔루션이 동일한 오류를 받았습니다 (감사합니다!). 약간 수정하는 것이 좋습니다.

  • objective-c category를 사용하여 새 메소드를 저장하십시오. 따라서 Item이 다시 생성되면 메소드를 잃지 않습니다.
  • 이미 변경 가능한 세트가 있는지 확인하십시오

내용 Item+category.m:

#import "Item+category.h"

@implementation Item (category)

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

@end

이 코드를 범주로 옮기는 것이 좋습니다. 그러나 여전히 @Dmitry Makarenko 답변과 같이 will / setPrimitiveValue / didChange 호출로 실제 추가 / 제거를 수용해야합니다.
블라디미르 Shutyuk

8

mogenerator를 사용하는 경우 대신

[parentObject add<Child>sObject:childObject];

간단히 사용하십시오 :

[[parent object <child>sSet] addObject:childObject];

mogenerator는 추가 코드를 처리하기 때문에 그렇지 않으면 기본 세트 객체에 대한 액세스를 작성하고 간단히 허용해야합니다.
Καrτhικ

수정 프로그램이 커밋 된 것 같습니다. 이것은 균질 발생기가 수정 된 몸체를 생성 할 것임을 의미합니다 ... github.com/dmakarenko/mogenerator/commit/…
조합

1
사용하고 mogenerator있지만 여전히 버그가 있습니다.
Colas

7

개인적으로 @Stephan의 다른 솔루션에 요약 된대로 CoreData 생성 메소드에 대한 호출을 메소드에 대한 직접 호출로 대체했습니다.

NSMutableOrderedSet* tempSet = [self mutableOrderedSetValueForKey:@"subitems"];
      [tempSet addObject:value];
[tempSet addObject:value];

이렇게하면 나중에 버그가 수정 될 때 Apple에서 생성 된 코드로 솔루션과 충돌 할 수있는 범주가 필요하지 않습니다.

여기에는 공식적인 방법이 추가되었습니다!


'[<CLASS 0x20886d10> valueForUndefinedKey :] :이 클래스는 키 하위 항목에 대해 키 값 코딩 호환이 아닙니다.'
jmstone617

애플의 알려진 문제 (나는 분명히 헛된 제스처에 대한 레이더를 열었 음)에 나열되지 않았다는 것을 여전히 말하지는 않지만이 솔루션은 나에게 완벽하게 작동했습니다.
Scott Corscadden

이 답변을 일찍 보았 으면 좋겠습니다. 나는 최근에 파기를 수행하고 마침내 당신이 여기에있는 것을 정확하게 구현할 때까지 가장 높은 투표 응답을 사용하고있었습니다 :)
Ja͢ck

addObject:두 번 불려지 는가?
Jason Moore

5

부모를 자식으로 설정하여 부모를 자식과 연결하면 다른 방법으로 충돌하지 않고 작동하는 것처럼 보입니다.

따라서 할 경우 :

[child setParent:parent]

대신에

[parent setChildObects:child]

그것은 적어도 iOS 7에서 작동하며 관계에 아무런 문제가 없었습니다.


1
양쪽이 다일 때별로 좋지 않습니다. 그렇다면 명확한 부모-자식 관계는 없습니다.
fatuhoku

3

나는 같은 문제가 있었지만, 내가하고있는 것과 다른 것을 시도했을 때만. 하위 항목의 코드를 볼 수 없지만 항목에 대한 역방향 링크가 있다고 가정합니다. 이 링크를 "parentItem"링크로 호출하면 가장 쉬운 해결책은 다음과 같습니다.

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;

그 결과 Apple 자체 코드를 사용하며 간단하고 깨끗합니다. 또한 세트가 자동으로 추가되고 모든 옵저버가 업데이트됩니다. 문제 없어요.


이거 좋은데. 전체 문제를 해결하고 순서대로 유지합니다. 버그가 여전히 존재한다는 것은 여전히 ​​미쳤다. 이 답변의 또 다른 이점은 코어 데이터 모델을 재생성하는 경우 버그 수정을 다시 작성할 필요가 없다는 것입니다. 감사!
Johan S

내 다른 답변을 참조하십시오. 더 자세하게 버그를 추적했습니다. 이것이 가장 쉬운 방법이지만 다른 방법은 더 많은 가능성을 열어주기 때문에 가장 좋습니다.
Owen Godfrey

와! 드디어!!! 감사! (다른 코드를 시도했지만 오류가 발생했습니다. 잘못된 유형에 대한 정보가 [self didChange : NSKeyValueChangeInsertion valuesAtIndexes : indexSet forKey : ChildrenKey]에 전송되었습니다.)
Leonard Pauli

3

방금이 문제를 파기하고 여기에 설명 된 다른 것보다 훨씬 간단한 구현을 사용하여 해결했습니다. NSManagedObject하위 클래스를 사용하지 않을 때 관계를 처리 하기 위해 사용 가능한 메소드를 사용하기 만합니다.

엔티티를 NSOrderedSet관계 에 삽입하기위한 구현 예 는 다음과 같습니다.

- (void)addAddress:(Address *)address
{
    if ([self.addresses containsObject:address]) {
        return;
    }
    // Use NSManagedObject's methods for inserting an object
    [[self mutableOrderedSetValueForKey:@"addresses"] addObject:address];
}

이것은 완벽하게 작동하며 NSManagedObject하위 클래스 로 이동하기 전에 사용하고 있던 것 입니다.


3

이 문제 는 XCode 7 으로 프로젝트를 Objective-C에서 Swift 2 로 마이그레이션하는 동안 발생했습니다 . 그 프로젝트는 예전에는 효과가 있었고, 그 이유는 다음과 같습니다. 나는이 버그를 고치기위한 대체 방법이있는 MOGenerator를 사용하고있었습니다. 그러나 모든 방법을 교체해야하는 것은 아닙니다.

기본 클래스에 최대한 의존하는 예제 클래스가 포함 된 완벽한 솔루션입니다.

주문한 항목이있는 목록이 있다고 가정 해 봅시다.

일대 다 관계가있는 경우 우선 빠른 승리 : 가장 쉬운 방법은 다음과 같습니다.

item.list = list

대신에

list.addItemsObject(item)

이제 이것이 옵션이 아닌 경우 다음과 같이 할 수 있습니다.

// Extension created from your DataModel by selecting it and
// clicking on "Editor > Create NSManagedObject subclass…"

extension List {
  @NSManaged var items: NSOrderedSet?
}

class List

  // Those two methods work out of the box for free, relying on
  // Core Data's KVC accessors, you just have to declare them
  // See release note 17583057 https://developer.apple.com/library/prerelease/tvos/releasenotes/DeveloperTools/RN-Xcode/Chapters/xc7_release_notes.html
  @NSManaged func removeItemsObject(item: Item)
  @NSManaged func removeItems(items: NSOrderedSet)

  // The following two methods usually work too, but not for NSOrderedSet
  // @NSManaged func addItemsObject(item: Item)
  // @NSManaged func addItems(items: NSOrderedSet)

  // So we'll replace them with theses

  // A mutable computed property
  var itemsSet: NSMutableOrderedSet {
    willAccessValueForKey("items")
    let result = mutableOrderedSetValueForKey("items")
    didAccessValueForKey("items")
    return result
  }

  func addItemsObject(value: Item) {
    itemsSet.addObject(value)
  }

  func addItems(value: NSOrderedSet) {
    itemsSet.unionOrderedSet(value)
  }
end

물론 Objective-C를 사용하는 경우 처음부터 아이디어를 얻었으므로 정확히 동일한 작업을 수행 할 수 있습니다. :)


3

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

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

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

Leelll, 해당 세트에 저장된 NSMutableOrderedSet 값의 사용자 정의 설정이 CoreData에 의해 데이터베이스에 올바르게 저장 될 것입니까? 나는 그것을 확인하지는 않았지만 CoreData는 NSOrderedSet에 대해 아무것도 모르고 많은 관계 컨테이너로 NSSet을 기대하는 것처럼 보입니다.


CoreData가 NSOrderedSet 객체를 반환하거나 가져 오려면이 시작된 질문에서 볼 수 있듯이 여러 조건이 충족되어야합니다. 내 코드를 공유하는 사람들이 Lion을 실행하지 않는 개발자 인 경우 가장 흔히 발생하는 실수입니다. snowleopard에서는 NSOrderedSets 프레임 워크를 사용할 수 없습니다. 그러나 그렇습니다.이 성능이 최고라고 확신하지는 않지만이 실패를 보지 못했습니다. 나는 이것이 전체 세트를 가져 와서 원하는 레코드를 삽입하는 대신 그것을 대체한다고 생각합니다.
InitJason

2

나는 모든 사람이 실제 문제를 놓치고 있다고 생각합니다. 접근 자 메서드 NSOrderedSet가 아니라의 하위 클래스가 아닌 사실 에 NSSet있습니다. 따라서 -interSectsSet:인수로 정렬 된 세트로 호출하면 실패합니다.

NSOrderedSet* setA = [NSOrderedSet orderedSetWithObjects:@"A",@"B",@"C",nil];
NSSet* setB = [NSSet setWithObjects:@"C",@"D", nil];

 [setB intersectsSet:setA];

실패 *** -[NSSet intersectsSet:]: set argument is not an NSSet

수정 사항은 세트 연산자의 구현을 변경하여 유형을 투명하게 처리하는 것 같습니다. 이유 -intersectsSet:가 순서가 있거나 순서가없는 세트로 작동 해야하는 이유는 없습니다 .

변경 알림에서 예외가 발생합니다. 아마도 역 관계를 처리하는 코드에서. 역 관계를 설정 한 경우에만 발생합니다.

다음은 나를 위해 트릭을했다

@implementation MF_NSOrderedSetFixes

+ (void) fixSetMethods
{
    NSArray* classes = [NSArray arrayWithObjects:@"NSSet", @"NSMutableSet", @"NSOrderedSet", @"NSMutableOrderedSet",nil];

    [classes enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
        NSString* name = obj;
        Class aClass = objc_lookUpClass([name UTF8String]);
        [MF_NSOrderedSetFixes fixMethodWithSetArgument:@selector(intersectsSet:) forClass:aClass];
        [MF_NSOrderedSetFixes fixMethodWithSetArgument:@selector(isSubsetOfSet:) forClass:aClass];
    }];
}

typedef BOOL (*BoolNSetIMP)(id _s,SEL sel, NSSet*);

/*
    Works for all methods of type - (BOOL) method:(NSSet*) aSet
*/
+ (void) fixMethodWithSetArgument:(SEL) aSel forClass:(Class) aClass 
{
    /* Check that class actually implements method first */
    /* can't use get_classInstanceMethod() since it checks superclass */
    unsigned int count,i;
    Method method = NULL;
    Method* methods = class_copyMethodList(aClass, &count);
    if(methods) {
        for(i=0;i<count;i++) {
            if(method_getName(methods[i])==aSel) {
                method = methods[i];
            }
        }
        free(methods);
    }
    if(!method) {
        return;
    }

   // Get old implementation
   BoolNSetIMP originalImp  = (BoolNSetIMP) method_getImplementation(method);
   IMP newImp = imp_implementationWithBlock(^BOOL(NSSet *_s, NSSet *otherSet) {
        if([otherSet isKindOfClass:[NSOrderedSet class]]) {
            otherSet = [(NSOrderedSet*)otherSet set];
        }
        // Call original implementation
        return originalImp(_s,aSel,otherSet);
    });
    method_setImplementation(method, newImp);
}
@end

2

방금 Swift (Xcode 6.1.1)에서 문제가 발생했습니다.

대답은 NSManagedObject 서브 클래스에 어떤 방법이나 추가 사항을 코딩하지 않았습니다 . 나는 그것이 컴파일러 실수라고 생각합니다. 매우 이상한 버그 ..

그것이 도움이되기를 바랍니다 ..


3
따라서 다른 수정 프로그램을 구현할 수 없으면이 문제를 해결하려면 어떻게해야합니까?
Ben Leggiero

2

나는 Inverse를 No Inverse로 설정 하여이 문제를 해결했습니다. 왜 애플 버그가 있는지 모르겠습니다.여기에 이미지 설명을 입력하십시오


1

"subitems"대신 "signals"라는 항목과 동일한 상황이 있습니다. tempset이있는 솔루션은 테스트에서 작동합니다. 또한 removeSignals : 메서드에 문제가있었습니다. 이 재정의가 작동하는 것 같습니다.

- (void)removeSignals:(NSOrderedSet *)values {
    NSMutableOrderedSet* tempset = [NSMutableOrderedSet orderedSetWithOrderedSet:self.signals];
    for (Signal* aSignal in values) {
        [tempset removeObject:aSignal];
    }
    self.signals = tempset;
}

더 좋은 방법이 있으면 알려주세요. 입력 한 값은 10 -20 개를 넘지 않으므로 성능은 큰 문제가되지 않습니다. 그럼에도 불구하고 관련 내용을 알려주십시오.

감사,

다미 엔


1

오류 메시지에 대한 인터넷 검색을 통해이 질문을 찾았으며 순서가 지정된 세트를 사용하지 않고 약간 다른 방식 으로이 오류가 발생했음을 지적했습니다. 이것은 주어진 질문에 대한 답변이 아니지만 검색하는 동안이 질문을 우연히 발견하는 다른 사람에게 도움이되는 경우를 대비하여 여기에 게시하고 있습니다.

새 모델 버전을 추가하고 기존 모델에 관계를 추가하고 헤더 파일에 add * Object 메소드를 직접 정의했습니다. 전화하려고 할 때 위의 오류가 발생했습니다.

내 모델을 검토 한 후에 "To-Many Relationship"체크 박스를 체크하는 것을 멍청하게 잊었 음을 깨달았습니다.

따라서이 문제가 발생하고 주문 세트를 사용하지 않는 경우 모델을 다시 확인하십시오.


1

나는이 버그에 대한 수정 사항을 찾았습니다. 나는 이것을 이것을 대체한다 :

[item addSubitemsObject:subItem];

이것으로 :

item.subitemsObject = subItem;

1

SWIFT에서 정답의 더 나은 버전

var tempSet = NSMutableOrderedSet()
if parent!.subItems != nil {
    tempSet = NSMutableOrderedSet(orderedSet: parent!.subItems!)
}

tempSet.add(newItem)
parent!.subItems = tempSet

0

LeeIII의 방법을 사용하는 것이 효과가 있지만 프로파일 링에서 속도가 상당히 느리다는 것을 알았습니다. 1000 개의 항목을 파싱하는 데 15 초가 걸렸습니다. 관계를 추가하는 코드를 주석 처리하면 15 초가 2 초로 바뀌 었습니다.

내 해결 방법 (더 빠르지 만 훨씬 추한)은 임시 변경 가능 배열을 만든 다음 모든 구문 분석이 완료 되면 정렬 된 세트로 복사하는 것입니다. (많은 관계를 추가하려는 경우에만 성능이 향상됩니다).

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

나는 이것이 다른 누군가를 돕기를 바랍니다!


0

로버트,

나는 당신의 대답이 이것에 효과적이라고 동의하지만, 관계에 전체 값 세트를 추가하기 위해 자동으로 생성 된 방법이 있음을 명심하십시오. (애플의 문서 여기 본 은 "일대 다 관계"섹션에서 또는 여기에 구현 그들에게이 방법을 "사용자 정의 일대 다 관계 소품 방법"섹션 아래)

- (void)addEmployees:(NSSet *)value
{
[self willChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueUnionSetMutation
      usingObjects:value];
[[self primitiveEmployees] unionSet:value];
[self didChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueUnionSetMutation
      usingObjects:value];
}

- (void)removeEmployees:(NSSet *)value
{
[self willChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueMinusSetMutation
      usingObjects:value];
[[self primitiveEmployees] minusSet:value];
[self didChangeValueForKey:@"employees"
      withSetMutation:NSKeyValueMinusSetMutation
      usingObjects:value];
}

핵심 데이터 외부에서 관계 집합을 쉽게 컴파일 한 다음이 방법을 사용하여 한 번에 추가 할 수 있습니다. 그것은 당신이 제안한 방법보다 덜 추악 할 수 있습니다 .)


당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.