Ngoại lệ được ném trong bộ truy cập đã tạo NSOrderedset


364

Trên ứng dụng Lion của tôi, tôi có mô hình dữ liệu này:

nhập mô tả hình ảnh ở đây

Mối quan hệ subitemsbên trong Item được ra lệnh .

Xcode 4.1 (build 4B110) đã tạo ra cho tôi những tập tin Item.h, Item.m, SubItem.hSubItem.h.

Đây là nội dung (tự phát) của 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

Và đây là nội dung (tự phát) của Item.m:

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

@implementation Item

@dynamic name;
@dynamic subitems;

@end

Như bạn có thể thấy, lớp Itemcung cấp một phương thức được gọi addSubitemsObject:. Thật không may, khi cố gắng sử dụng nó theo cách này:

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

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

[item addSubitemsObject:subItem];

lỗi này xuất hiện:

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

Bạn có thể giúp tôi được không?

Cập nhật:

Chỉ sau 1.787 ngày kể từ báo cáo lỗi của tôi, hôm nay (1 tháng 8 năm 2016) Apple đã viết cho tôi điều này: "Vui lòng xác minh sự cố này với bản dựng iOS 10 beta mới nhất và cập nhật báo cáo lỗi của bạn tại bugreport.apple.com với kết quả của bạn." . Hãy hy vọng đây là thời điểm thích hợp :)


5
Tôi đang thấy vấn đề tương tự. Hy vọng nó sẽ được sửa sớm. Mặc dù sử dụng bộ lệnh có thể thay đổi được đặt trực tiếp là một cách giải quyết dễ dàng trong thời điểm hiện tại. Lưu ý: Tôi đang sử dụng mogenerator, nhưng tôi cho rằng nó đang sử dụng cùng một trình tạo Apple trong nội bộ cho phần mã được tạo này.
Chad Podoski

12
Đã gần 2 năm! Bạn sẽ sửa nó trong iOS 7, Apple chứ? Tôi chỉ muốn chia sẻ với những người thắc mắc liệu lỗi này có còn không: "Vâng, đúng vậy."
an0

1
Rất gần hai năm nay, đây vẫn là một vấn đề trong tất cả các bản xem trước của nhà phát triển xcode 5.
Korvin Szanto

2
Bạn vẫn thấy vấn đề nếu bạn sử dụng bộ truy cập KVC thích hợp? (tức là mutableOrderedSetValueForKey:)
quellish

3
Xuất hiện vẫn là một vấn đề trên Mavericks.
Tim

Câu trả lời:


263

Tôi đã sao chép thiết lập của bạn cả với mô hình dữ liệu của bạn và một trong những tên riêng của tôi với các tên khác nhau. Tôi đã nhận được cùng một lỗi trong cả hai trường hợp.

Trông giống như một lỗi trong mã tự phát của Apple.


60
ID lỗi là 10114 310. Nó đã được báo cáo vào ngày 13 tháng 9 năm 2011 nhưng hôm nay (15 tháng 1 năm 2012) nó vẫn "mở". Thật không thể tin được, xem xét số lượng người có cùng một vấn đề.
Dev

14
Cập nhật: ngày hôm nay (11 tháng 5 năm 2012), lỗi # 10114 310 vẫn mở, sau 240 ngày kể từ báo cáo của tôi (ngày 13 tháng 9 năm 2011). Không thể tin được.
Dev

23
Tôi vừa mới nói chuyện này với một kỹ sư của Apple trong một trong các phiên thí nghiệm CoreData Lab tại WWDC. Họ thừa nhận vấn đề và đó là một lỗi chính hãng, và từ những gì tôi thấy nó có tình trạng "nghiêm trọng", nhưng tất nhiên không có lời hứa nào khi họ sẽ sửa nó. Tôi không nghĩ rằng nó sẽ được sửa trong iOS6 / Mountain Lion. Tôi nghĩ sẽ tốt hơn nếu nhân đôi radar này hơn nữa. Hiện tại nó có khoảng 25 dup, càng nhiều càng tốt!
DaGaMs

40
Chỉ cần kiểm tra ngày hôm nay, nó vẫn còn trong iOS 7 GM / OMG! Tôi không thể tin được điều đó
an0

79
Cập nhật: 797 ngày, 2 phiên bản iOS chính mới và vô số phiên bản Xcode đã trôi qua kể từ khi tôi lấp đầy lỗi # 10114 310. Và nó vẫn còn "mở". Không thể tin được.
Dev

244

Tôi đồng ý rằng có thể có một lỗi ở đây. Tôi đã sửa đổi việc triển khai trình thiết lập thêm đối tượng để chắp thêm chính xác vào NSMutableOrderedset.

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

Việc chỉ định lại tập hợp thành self.subitems sẽ đảm bảo rằng các thông báo Will / DidChangeValue được gửi.


Đoạn mã của bạn là thứ tôi cần để giải quyết vấn đề này. Hy vọng Apple sẽ khắc phục vấn đề này, nhưng cho đến nay tôi vẫn chưa thấy bất kỳ vấn đề nào khi sử dụng phương pháp của bạn.
Christopher Hujanen

Tôi gặp lỗi này khi tôi cố gắng thực hiện công việc này .. [__NSArrayI isEqualTo Set:]: bộ chọn không được nhận dạng được gửi đến ví dụ ... Đây thường là từ một mục đã được phát hành, nhưng không thể tìm thấy ở đâu, bất cứ ai chạy vào đây?
DerekH

@DerekH isEqualToset là một phương thức chỉ NSSet có, do đó, tôi đoán là bạn đã chuyển đổi, tạo hoặc đang xử lý một con trỏ như một NSArray trước khi quay trở lại NSManagedObject, nếu có bất kỳ lý do nào cần gọi isEqualToOrderedset thậm chí thay đổi hoặc để nguyên như vậy.
initJason

3
@MarkAmery Đã kiểm tra. Đã xác minh. Self.subitems setter động sẽ gửi các thông báo. Vậy giải pháp JLust là chính xác.
bernstein

3
Đây là một câu trả lời tốt, nhưng nó không hiệu quả. Bạn sao chép toàn bộ tập hợp đã đặt hàng, sửa đổi và sau đó sao chép lại. Hiệu ứng không chỉ là một cú hích đối với tập hợp được đặt hàng, mà thông báo được gửi đi rằng mỗi khi thay đổi tập hợp được đặt hàng, toàn bộ nội dung của nó sẽ bị thay đổi! Nếu bộ được đặt hàng này được sử dụng cho một UITable chẳng hạn, điều này có thể có sự phân nhánh nghiêm trọng để cập nhật. Tôi đã phác thảo trong giải pháp của mình chính xác lỗi xuất phát từ đâu và tôi đã chỉ ra một phương pháp hiệu quả hơn để khắc phục lỗi.
Owen Godfrey

111

Tôi đã quyết định cải tiến giải pháp bằng cách thực hiện tất cả các phương pháp cần thiết:

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
Loại tai nạn là gì? 'removeObjectFromSubitemsAtIndex' không xóa các mục con này, chúng vẫn tồn tại trong bộ lưu trữ của bạn, đó chỉ là cách để xóa mối quan hệ giữa các đối tượng.
Dmitry Makarenko

2
kItemsKey là một hằng số được thêm vào chỉ để thuận tiện trong các cuộc gọi phương thức KVO. Đó là tên của mối quan hệ được sắp xếp mà bạn đang viết phương thức của mình.
Dmitry Makarenko

1
Đó là những gì tôi nghĩ rằng nó là. Cảm ơn. Nhưng vấn đề của tôi là dữ liệu không được lưu vào cơ sở dữ liệu bằng cách sử dụng các phương pháp này.
Bagusflyer

4
!!!!!!!!! Chỉ cần sao chép mã và thay đổi tên phương thức, nó hoạt động hoàn hảo !!! Đây là câu trả lời nhanh nhất.
flypig

1
Điều này là tuyệt vời, nhưng việc tạo bản sao tạm thời của bộ đã ra lệnh là không cần thiết. Thủ phạm là willChangeValueForKey:withSetMutation:usingObjects, mà bạn đã tránh thành công. Sau đó, chỉ cần sử dụng [[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values]hoặc [[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values]khi thích hợp. Xem câu trả lời của tôi để biết chi tiết.
Owen Godfrey

38

Vâng, đây chắc chắn là một lỗi dữ liệu cốt lõi. Tôi đã viết lên một bản sửa lỗi dựa trên ObjC-Runtime một lúc trước, nhưng tại thời điểm đó tôi đã hình dung nó sẽ được sửa sớm. Dù sao, không có may mắn như vậy, vì vậy tôi đã đăng nó lên GitHub với tên KCOrderedAccessorFix . Giải quyết vấn đề trên tất cả các thực thể của bạn:

[managedObjectModel kc_generateOrderedSetAccessors];

Một thực thể cụ thể:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

Hoặc chỉ cho một mối quan hệ:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];

Tôi tự hỏi liệu điều này sẽ xung đột với bản sửa lỗi thực sự từ Apple hay không?
tia

3
Điều này không nên xung đột với sửa lỗi của Apple, vì mục đích của nó là ghi đè lên việc thực hiện của Apple bất kể điều gì. Khi / nếu điều này thực sự được Apple khắc phục, có thể tôi sẽ thêm một - (BOOL)kc_needsOrderedSetAccessorFix;hoặc thứ gì đó kiểm tra phiên bản Foundation / iOS.
Sterling Archer

2
Có một KCOrderedAccessorFix.podspec đã có trong repo chính của Cốc Cốc. Vì vậy, để liên kết điều này với các dự án của bạn, bạn chỉ cần thêm "pod 'KCOrderedAccessorFix'" vào Podfile của mình
Anton Matosov

Điều này có một số vấn đề với iOS 8 (chữ ký phương thức không chính xác objc_msg_send)
NSTJ

Trên iOS9 nó hoạt động, công việc tốt! Đây là giải pháp tốt nhất từ ​​trước đến nay, không cần thay đổi bất cứ điều gì trong mã của bạn!
Borzh

32

Thay vào đó để tạo một bản sao, tôi đề nghị sử dụng trình truy cập trong NSObject để có quyền truy cập vào NSMutableOrderedset của các mối quan hệ.

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

ví dụ: Ghi chú phát hành dữ liệu cốt lõi cho iOS v5.0 đề cập đến điều này.

Trong một thử nghiệm ngắn, nó đã làm việc trong ứng dụng của tôi.


1
Không thể cấu trúc lại chuỗi ký tự dễ dàng. Trình biên dịch có thể gõ kiểm tra self.subitems nếu bạn sử dụng mã.
logancautrell

1
@logancautrell vâng điều này đúng. Nó phụ thuộc vào mức độ ưu tiên của trường hợp sử dụng cụ thể. Nói chung tôi tập trung vào việc tiết kiệm tài nguyên đặc biệt là trong trường hợp này vì đây chỉ là một cách giải quyết.
Stephan

2
Chuỗi chữ có thể được thay thế bằng NSStringFromSelector(@selector(subitems))mặc dù :)
Ja͢ck

17

Tôi đã theo dõi lỗi. Nó xảy ra trongwillChangeValueForKey:withSetMutation:usingObjects: .

Cuộc gọi này đặt ra một chuỗi các thông báo có thể khó theo dõi và tất nhiên những thay đổi đối với một phản hồi này có thể có ý nghĩa đối với một thông báo khác, điều mà tôi nghi ngờ là tại sao Apple không làm gì cả.

Tuy nhiên, nó ổn trong Set và chỉ các thao tác Set trên Orderedset bị trục trặc. Điều đó có nghĩa là chỉ có bốn phương pháp cần được thay đổi. Do đó, tất cả những gì tôi đã làm là chuyển đổi các hoạt động Set thành các hoạt động Array tương đương của chúng. Chúng hoạt động hoàn hảo và tối thiểu (nhưng cần thiết) tổng phí.

Ở mức độ quan trọng, giải pháp này bị một lỗ hổng nghiêm trọng; nếu bạn đang thêm các đối tượng và một trong các đối tượng đã tồn tại, thì nó sẽ không được thêm hoặc di chuyển vào phía sau danh sách được sắp xếp (tôi không biết cái nào). Trong cả hai trường hợp, chỉ số theo thứ tự dự kiến ​​của đối tượng tại thời điểm chúng tôi đến didChangekhác với những gì đã được dự đoán. Điều này có thể phá vỡ một số ứng dụng của mọi người, nhưng nó không ảnh hưởng đến tôi, vì tôi chỉ thêm các đối tượng mới hoặc tôi xác nhận vị trí cuối cùng của chúng trước khi tôi thêm chúng.

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

Tất nhiên, có một giải pháp dễ dàng hơn. nó là như sau;

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

Quá xấu mọi người khác dường như đã bỏ qua câu trả lời này, chắc chắn có vẻ như là giải pháp tốt nhất.
George

Giải pháp này có hiệu suất tốt hơn nhiều so với giải pháp sử dụng orderWithOrderedset để tạo một bộ cục bộ. Điều đó có một chi phí lớn khi bạn có bộ dữ liệu lớn. Giải pháp dễ dàng hơn dường như chỉ là một phiên bản được cấu trúc lại của phiên bản ban đầu với các phương thức không được hiển thị.
David Pettigrew

1
Tôi vẫn thấy một vụ tai nạn trong addChildren: *** Chấm dứt ứng dụng do ngoại lệ còn tự do 'NSInvalidArgumentException', lý do: '- [insertTrackpoints TrackHistory: atIndexes:]: chọn không được công nhận gửi đến dụ 0x1702b1b20'
Victor Bogdan

@OwenGodfrey Để giải pháp dễ dàng hơn, bạn đang thực hiện các phương pháp này ở đâu? Tôi đang nhận được một ngoại lệ: [Parent insertObject: inChildrenAt Index:] bộ chọn không được nhận dạng được gửi đến phiên bản 0x6180000ac480.
Dalmazio

biến của bạn là "Parent" với số vốn "P"? Điều đó có nghĩa là bạn đang gọi lớp "Parent" hay bạn có một biến thể hiện có tên "Parent" không? Nếu lớp của tôi là Parent, thì tôi đã triển khai các phương thức này ở dưới cùng của Parent, nhưng bạn sẽ cần gọi nó theo một thể hiện, nhiều khả năng sẽ được đặt tên là "Parent" với chữ "p", vì đây không phải là các phương thức lớp .
Owen Godfrey

10

Tài liệu của Apple cho nhiều mối quan hệ cho biết: bạn nên truy cập bộ có thể thay đổi proxy hoặc bộ được đặt hàng bằng cách sử dụng

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

Sửa đổi bộ này sẽ thêm hoặc xóa quan hệ với đối tượng được quản lý của bạn. Truy cập bộ được đặt hàng có thể thay đổi bằng cách sử dụng bộ truy cập cho dù với [] hoặc. ký hiệu là sai và sẽ thất bại.


3
Công bằng mà nói, các tài liệu cũng nói: "hoặc một trong các phương thức trình biến đổi mối quan hệ được tạo tự động (xem Phương thức truy cập được tạo động):"
Matt

Được rồi được rồi ... bạn nói đúng. Vậy thì, hãy nói rằng đó là cách làm việc đơn giản nhất ...
Nicolas Manzini

9

Nhận được lỗi tương tự, giải pháp @LeeIII đã làm việc cho tôi (cảm ơn!). Tôi đề nghị sửa đổi một chút nó:

  • sử dụng danh mục object-c để lưu trữ phương thức mới (vì vậy chúng tôi sẽ không mất phương thức của mình nếu Mục được tạo lại)
  • kiểm tra xem chúng ta đã có bộ có thể thay đổi chưa

Nội dung của 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

Điểm tốt để di chuyển mã này vào thể loại. Nhưng chúng ta vẫn cần phải thực hiện thêm / xóa thực tế với các lệnh gọi will / setPrimitiveValue / didChange như trong câu trả lời @Dmitry Makarenko.
Vladimir Shutyuk

8

Nếu bạn đang sử dụng mogenerator, thì thay vì

[parentObject add<Child>sObject:childObject];

chỉ cần sử dụng:

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

Bởi vì mogenerator chăm sóc mã bổ sung mà bạn sẽ phải viết và đơn giản là cho phép truy cập vào đối tượng thiết lập bên dưới.
Καrτhικ

Có vẻ như một bản sửa lỗi vừa được cam kết có nghĩa là mogenerator sẽ tạo ra các cơ quan được sửa chữa ... github.com/dmakarenko/mogenerator/commit/ trộm
tổ hợp

1
Tôi đang sử dụng mogeneratornhưng tôi vẫn có lỗi.
Colas

7

Cá nhân tôi vừa thay thế các cuộc gọi đến các phương thức được tạo bởi CoreData bằng các cuộc gọi trực tiếp đến phương thức như được nêu trong một giải pháp khác bởi @Stephan:

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

Điều này loại bỏ sự cần thiết của các danh mục mà sau này có thể xung đột với giải pháp từ Apple sang mã được tạo khi lỗi được khắc phục.

Điều này có thêm điểm cộng là cách chính thức để làm điều đó!


Điều này đưa ra lỗi sau: '[<Class 0x20886d10> valueForUnd xác địnhKey:]: lớp này không phải là giá trị khóa tuân thủ mã hóa cho các phần phụ khóa.'
jmstone617

Mặc dù tôi vẫn không thể phủ nhận rằng điều này không được liệt kê trong các vấn đề đã biết của Apple (tôi đã mở một radar cho cử chỉ rõ ràng vô ích đó), giải pháp này đã hoạt động hoàn hảo với tôi.
Scott Corscadden

Ước gì tôi đã thấy câu trả lời này sớm hơn; Tôi đã sử dụng câu trả lời được bình chọn cao nhất cho đến khi gần đây tôi thực hiện một số hoạt động đào và cuối cùng đã thực hiện chính xác những gì bạn có ở đây :)
Ja͢ck

Tại sao được addObject:gọi là hai lần?
Jason Moore

5

Có vẻ như nếu bạn liên kết cha mẹ với đứa trẻ bằng cách đặt cha mẹ cho đứa trẻ và không phải là cách khác xung quanh nó hoạt động mà không gặp sự cố.

Vì vậy, nếu bạn làm:

[child setParent:parent]

thay vì

[parent setChildObects:child]

Nó nên hoạt động, ít nhất là nó hoạt động trên iOS 7 và không có vấn đề gì với mối quan hệ.


1
Không làm được gì nhiều khi cả hai bên đều nhiều. Sau đó, không có mối quan hệ cha-con rõ ràng.
fatuhoku

3

Tôi đã có cùng một vấn đề, nhưng chỉ khi tôi thử một cái gì đó khác với những gì tôi đã làm. Tôi không thể xem mã cho subItem, nhưng tôi sẽ cho rằng nó có liên kết ngược với mục. Hãy gọi liên kết hoàn nguyên này là "ParentItem", thì giải pháp đơn giản nhất là:

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;

Hiệu quả là nó sử dụng mã riêng của apple và nó đơn giản và sạch sẽ. Ngoài ra, bộ được tự động thêm vào và tất cả các quan sát viên được cập nhật. Không vấn đề gì.


Điều này là rất tốt đẹp. Nó giải quyết toàn bộ vấn đề và giữ cho nó được ra lệnh. Vẫn điên rồ rằng lỗi vẫn còn. Một lợi ích khác của câu trả lời này là nếu bạn tạo lại các mô hình dữ liệu cốt lõi của mình, bạn không phải viết lại các bản sửa lỗi. Cảm ơn!
Johan S

Xem câu trả lời khác của tôi. Tôi đã theo dõi lỗi chi tiết hơn. Đây vẫn là cách dễ nhất, nhưng phương pháp khác là tốt nhất vì nó mở ra nhiều khả năng hơn.
Owen Godfrey

Ồ Cuối cùng !!! Cảm ơn! (Đã thử mã khác của bạn, nhưng đã gặp lỗi, một số lỗi về loại sai đó đã được gửi tại [self didChange: NSKeyValueChangeInserts valueAt Indexes: indexset forKey: ChildrenKey];)
Leonard Pauli

3

Tôi vừa phạm lỗi về vấn đề này, và giải quyết nó bằng cách sử dụng một cách thực hiện đơn giản hơn nhiều so với những vấn đề khác được nêu ở đây. Tôi chỉ đơn giản là sử dụng các phương thức có sẵn trênNSManagedObject để xử lý các mối quan hệ khi không sử dụng các lớp con.

Một triển khai ví dụ để chèn một thực thể vào một NSOrderedSetmối quan hệ sẽ như thế này:

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

Điều này hoạt động hoàn hảo, và là những gì tôi đã sử dụng trước khi tôi chuyển sang các NSManagedObjectlớp con.


3

Vấn đề này xảy ra với tôi khi di chuyển một dự án từ Objective-C sang Swift 2 bằng XCode 7 . Dự án đó đã từng hoạt động và vì một lý do chính đáng: Tôi đã sử dụng MOGenerator có các phương pháp thay thế để sửa lỗi này. Nhưng không phải tất cả các phương pháp đều yêu cầu thay thế.

Vì vậy, đây là giải pháp hoàn chỉnh với một lớp ví dụ, dựa vào các bộ truy cập mặc định càng nhiều càng tốt.

Giả sử chúng ta có một Danh sách với các mục đã đặt hàng

Đầu tiên là một chiến thắng nhanh chóng nếu bạn có một mối quan hệ một / nhiều, dễ nhất là chỉ cần làm:

item.list = list

thay vì

list.addItemsObject(item)

Bây giờ, nếu đó không phải là một lựa chọn , đây là những gì bạn có thể làm:

// 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

Tất nhiên, nếu bạn đang sử dụng Objective-C, bạn có thể thực hiện chính xác điều tương tự vì đây là nơi tôi có ý tưởng ở nơi đầu tiên :)


3

Tôi đồng ý rằng có thể có một lỗi ở đây. Tôi đã sửa đổi việc triển khai thêm đối tượng> setter để chắp thêm chính xác vào NSMutableOrderedset.

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

Việc chỉ định lại tập hợp thành self.subitems sẽ đảm bảo rằng các thông báo Will / DidChangeValue> được gửi.

Leelll, bạn có chắc chắn rằng sau khi thiết lập tùy chỉnh các giá trị NSMutableOrderedset được lưu trữ trong bộ đó sẽ được CoreData lưu vào cơ sở dữ liệu một cách chính xác không? Tôi đã không kiểm tra điều đó, nhưng có vẻ như CoreData không biết gì về NSOrderedset và mong đợi NSSet là nơi chứa nhiều mối quan hệ.


Để CoreData trả lại hoặc lấy một đối tượng NSOrderedset, nhiều điều kiện phải được đáp ứng vì câu hỏi bắt đầu này đã được hiển thị. Những lỗi phổ biến nhất tôi thấy khi mọi người chia sẻ mã của tôi là nhà phát triển không chạy Lion. Khung NSOrderedSets không khả dụng trên snowleopard. Nhưng vâng, tôi chưa thấy thất bại này, mặc dù tôi không chắc đây là hiệu suất tốt nhất. Tôi đoán điều này sẽ lấy toàn bộ và thay thế nó thay vì chỉ chèn bản ghi mong muốn.
initJason

2

Tôi nghĩ mọi người đang thiếu vấn đề thực sự. Nó không nằm trong các phương thức accessor mà thực tế NSOrderedSetkhông phải là một lớp con của NSSet. Vì vậy, khi -interSectsSet:được gọi với một tập hợp được đặt làm đối số, nó thất bại.

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

 [setB intersectsSet:setA];

thất bại với *** -[NSSet intersectsSet:]: set argument is not an NSSet

Có vẻ như sửa lỗi là thay đổi việc thực hiện các toán tử tập hợp để chúng xử lý các kiểu trong suốt. Không có lý do tại sao một-intersectsSet: nên làm việc với một tập hợp có trật tự hoặc không có thứ tự.

Ngoại lệ xảy ra trong thông báo thay đổi. Có lẽ trong mã xử lý mối quan hệ nghịch đảo. Vì nó chỉ xảy ra nếu tôi thiết lập một mối quan hệ nghịch đảo.

Sau đây đã lừa tôi

@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

Tôi vừa gặp sự cố trong Swift (Xcode 6.1.1).

Câu trả lời là KHÔNG MÃ BẤT K MET PHƯƠNG PHÁP NÀO HAY THÊM trong các lớp con NSManagedObject của bạn. Tôi nghĩ đó là một lỗi biên dịch. Lỗi rất lạ ..

Hy vọng nó giúp ..


3
Vì vậy, nếu tôi không thể thực hiện các sửa lỗi khác, tôi nên làm gì để khắc phục điều này?
Ben Leggiero

2

Tôi đã giải quyết vấn đề này bằng cách đặt nghịch đảo thành Không nghịch đảo, tôi không biết tại sao, Có thể có Lỗi của Apple.nhập mô tả hình ảnh ở đây


1

Tôi có tình huống tương tự với một mục gọi là "tín hiệu" thay vì "phụ". Các giải pháp với tempset hoạt động trong thử nghiệm của tôi. Hơn nữa, tôi đã gặp vấn đề với phương thức removeSignals :. Ghi đè này dường như hoạt động:

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

Nếu có một cách tốt hơn để làm điều này, xin vui lòng cho tôi biết. Giá trị đầu vào của tôi không bao giờ nhiều hơn 10 -20 mặt hàng vì vậy hiệu suất không phải là vấn đề đáng lo ngại - dù sao, vui lòng chỉ ra bất cứ điều gì có liên quan.

Cảm ơn,

Damien


1

Tôi đã tìm thấy câu hỏi này bằng cách googling cho thông báo lỗi và chỉ muốn chỉ ra rằng tôi gặp phải lỗi này theo một cách hơi khác (không sử dụng các bộ được đặt hàng). Đây không phải là một câu trả lời cho câu hỏi đã cho, nhưng tôi sẽ đăng nó ở đây chỉ trong trường hợp nó hữu ích cho bất kỳ ai khác vấp phải câu hỏi này trong khi tìm kiếm.

Tôi đã thêm một phiên bản mô hình mới và thêm một số mối quan hệ vào các mô hình hiện có và tự xác định các phương thức add * Object trong tệp tiêu đề. Khi tôi cố gắng gọi cho họ, tôi đã gặp lỗi ở trên.

Sau khi xem xét các mô hình của tôi, tôi nhận ra rằng tôi đã quên một cách ngu ngốc để kiểm tra hộp kiểm "Nhiều mối quan hệ".

Vì vậy, nếu bạn đang gặp phải vấn đề này và bạn không sử dụng các bộ được đặt hàng, hãy kiểm tra kỹ mô hình của bạn.


1

Tôi tìm thấy một sửa chữa cho lỗi này hoạt động cho tôi. Tôi chỉ thay thế điều này:

[item addSubitemsObject:subItem];

Với cái này:

item.subitemsObject = subItem;

1

Phiên bản tốt hơn của câu trả lời đúng trong SWIFT

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

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

0

Tôi tìm thấy bằng cách sử dụng phương pháp của LeeIII, nhưng trên hồ sơ tìm thấy nó rất chậm. Phải mất 15 giây để phân tích 1000 mục. Nhận xét mã để thêm mối quan hệ biến 15 giây thành 2 giây.

Cách giải quyết của tôi (nhanh hơn nhưng xấu hơn nhiều) liên quan đến việc tạo một mảng có thể thay đổi tạm thời sau đó sao chép vào tập hợp được sắp xếp khi tất cả quá trình phân tích cú pháp được thực hiện. (đây chỉ là một chiến thắng hiệu suất nếu bạn sẽ thêm nhiều mối quan hệ).

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

Tôi hy vọng điều này sẽ giúp người khác!


0

Robert,

Tôi đồng ý câu trả lời của bạn sẽ có tác dụng cho việc này, nhưng hãy nhớ rằng đã có một phương thức được tạo tự động để thêm toàn bộ giá trị vào một mối quan hệ. Tài liệu của Apple ( như được thấy ở đây trong phần "Nhiều mối quan hệ" hoặc ở đây trong phần "Phương pháp truy cập nhiều mối quan hệ tùy chỉnh") thực hiện theo cách này:

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

Bạn có thể dễ dàng biên dịch tập hợp các mối quan hệ của mình bên ngoài dữ liệu cốt lõi và sau đó thêm tất cả chúng cùng một lúc bằng phương pháp này. Nó có thể ít xấu hơn phương pháp bạn đề xuất;)


Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.