Исключение, генерируемое в сгенерированных средствах доступа NSOrderedSet


364

В моем приложении Lion у меня есть эта модель данных:

введите описание изображения здесь

Отношения subitemsвнутри Item упорядочены .

Xcode 4.1 (сборка 4B110) создал для меня файл Item.h, Item.m, SubItem.hи SubItem.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

Вы можете помочь мне?

Обновить:

Спустя всего 1787 дней после моего отчета об ошибках, сегодня (1 августа 2016 года) Apple написала мне следующее: «Пожалуйста, проверьте эту проблему с последней бета-версией iOS 10 и обновите свой отчет об ошибке на bugreport.apple.com со своими результатами». , Будем надеяться, что сейчас самое время :)


5
Я вижу ту же проблему. Надеюсь, это скоро исправят. Хотя использование изменяемого упорядоченного набора напрямую - это простой обходной путь. Примечание: я использую mogenerator, но я предполагаю, что он использует тот же генератор Apple, что и для этой части сгенерированного кода.
Чад Подоски

12
Это почти 2 года! Вы исправите это в iOS 7, Apple? —— Я просто хочу поделиться с теми, кто интересуется, есть ли еще эта ошибка: «Да, это так».
AN0

1
Уже почти два года, это все еще проблема во всех предварительных версиях для разработчиков xcode 5.
Корвин Санто

2
Вы все еще видите проблему, если используете соответствующий метод доступа KVC? (то есть mutableOrderedSetValueForKey:)
неприветливое

3
Кажется, все еще проблема на Маверикс.
Тим

Ответы:


263

Я воспроизвел ваши настройки как с вашей моделью данных, так и с моей собственной, с разными именами. Я получил ту же ошибку в обоих случаях.

Похоже, ошибка в автоматически сгенерированном коде Apple.


60
Идентификатор ошибки - 10114310. Сообщалось 13 сентября 2011 г., но сегодня (15 января 2012 г.) он все еще "открыт". Это невероятно, учитывая количество людей, у которых такая же проблема.
Dev

14
Обновление: сегодня (11 мая 2012 г.) ошибка # 10114310 все еще открыта, через 241 день после моего отчета (13 сентября 2011 г.). Невероятно.
Дев

23
Я только что рассказал об этом инженеру Apple во время одной из сессий Лаборатории CoreData на WWDC. Они признают проблему, и что это подлинная ошибка, и из того, что я видел, она имеет «критический» статус, но, конечно, нет никаких обещаний относительно того, когда они ее исправят. Я не думаю, что это будет исправлено в iOS6 / Mountain Lion. Я думаю, что было бы хорошо дублировать этот радар дальше. В настоящее время у него около 25 дупов, чем больше, тем лучше!
DaGaMs

40
Только что проверил сегодня, он все еще там в iOS 7 GM / OMG! Я не могу в это поверить ...
an0

79
Обновление: 797 дней, 2 новые версии iOS и бесчисленное множество версий XCode прошли с тех пор, как я заполнил ошибку # 10114310. И это все еще "открыто". Невероятно.
Дев

244

Я согласен, что здесь может быть ошибка. Я изменил реализацию установщика объекта добавления, чтобы правильно добавить к NSMutableOrderedSet.

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

Переназначение набора для self.subitems обеспечит отправку уведомлений Will / DidChangeValue.


Ваш фрагмент кода был как раз тем, что мне нужно, чтобы разобраться с этой проблемой. Надеюсь, что Apple в конечном итоге исправит проблему, но пока я не видел никаких проблем с использованием вашего подхода.
Кристофер Хуянен,

Я получаю эту ошибку, когда пытаюсь реализовать этот обходной путь. [__NSArrayI isEqualToSet:]: нераспознанный селектор, отправленный экземпляру ... Обычно это элемент, который был выпущен, но не может найти, где кто-либо запускает в это?
ДерекХ

@DerekH isEqualToSet - это метод, который есть только в NSSet, поэтому я предполагаю, что вы преобразовали, создали или обрабатываете указатель как NSArray перед тем, как вернуться к NSManagedObject, который должен по любой причине вызывать isEqualToOrderedSet, чтобы определить, требуется ли для набора даже изменить или остаться как есть.
InitJason

3
@MarkAmery Протестировано. Проверенные. Динамический установщик self.subitems отправляет уведомления. Таким образом, решение JLust является правильным.
Бернштейн

3
Это хороший ответ, но неэффективный. Вы копируете весь заказанный набор, изменяете и затем копируете его обратно. Эффект - это не просто попадание в упорядоченный набор, а отправка уведомлений о том, что при каждом изменении упорядоченного набора изменяется все его содержимое! Если этот упорядоченный набор используется, например, для UITable, это может иметь серьезные последствия для обновления. В своем решении я обрисовал, откуда именно возникла ошибка, и показал более эффективный метод обхода ошибки.
Оуэн Годфри

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' не удаляет эти подэлементы, они все еще существуют в вашем хранилище, это просто способ удалить связь между объектами.
Дмитрий Макаренко

2
kItemsKey - это константа, которая была добавлена ​​просто для удобства в вызовах методов KVO. Это название упорядоченных отношений, для которых вы пишете свои методы.
Дмитрий Макаренко

1
Это то, что я думаю. Спасибо. Но моя проблема в том, что данные не сохраняются в базу данных с помощью этих методов.
Bagusflyer

4
!!!!!!!!! Просто скопировав код и изменив имена методов, он отлично работает !!! Это самый быстрый ответ.
flypig

1
Это потрясающе, но создание временной копии заказанного набора не требуется. Виновником является то willChangeValueForKey:withSetMutation:usingObjects, чего вы успешно избежали. После этого просто используйте [[self primitiveValueForKey:ChildrenKey] unionOrderedSet:values]или [[self primitiveValueForKey:ChildrenKey] minusOrderedSet:values]по необходимости. Смотрите мой ответ для деталей.
Оуэн Годфри

38

Да, это определенно ошибка Базовых данных. Некоторое время назад я написал исправление на основе ObjC-Runtime, но в то время я думал, что оно будет исправлено в ближайшее время. В любом случае, не повезло, поэтому я разместил его на GitHub как KCOrderedAccessorFix . Обойти проблему на всех ваших сущностях:

[managedObjectModel kc_generateOrderedSetAccessors];

Одна сущность в частности:

[managedObjectModel kc_generateOrderedSetAccessorsForEntity:entity];

Или только для одного отношения:

[managedObjectModel kc_generateOrderedSetAccessorsForRelationship:relationship];

Интересно, будет ли это конфликтовать с реальным исправлением от Apple или нет?
ТИА

3
Это не должно вступать в противоречие с исправлением Apple, поскольку его цель - отменить реализацию Apple, несмотря ни на что. Когда / если это на самом деле исправлено Apple, возможно, я добавлю - (BOOL)kc_needsOrderedSetAccessorFix;или что-то, что проверяет версию Foundation / iOS.
Стерлинг Арчер

2
Уже есть KCOrderedAccessorFix.podspec в основном репо CocoaPods. Поэтому, чтобы связать это с вашими проектами, вы можете просто добавить «pod 'KCOrderedAccessorFix'" в ваш Podfile
Антон Матосов

У этого были некоторые проблемы с iOS 8 (неправильные подписи метода для objc_msg_send)
NSTJ

На iOS9 все работает, отличная работа! Это лучшее решение, не нужно ничего менять в своем коде!
Борж

32

Вместо того чтобы делать копию, я предлагаю использовать аксессор в NSObject, чтобы получить доступ к NSMutableOrderedSet отношений.

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

например, примечания к выпуску Core Data для iOS v5.0 ссылаются на это.

В коротком тесте это сработало в моем приложении.


1
Не могу так просто рефакторинг буквальных строк. Компилятор может напечатать check self.subitems, если вы используете код.
logancautrell

1
@logancautrell да, это правильно. Это зависит от приоритета конкретного варианта использования. В общем, я сосредоточен на экономии ресурсов, особенно в этом случае, потому что это был просто обходной путь.
Стефан

2
Символьная строка может быть заменена NSStringFromSelector(@selector(subitems))хотя :)
JACK

17

Я отследил ошибку. Это происходит в willChangeValueForKey:withSetMutation:usingObjects:.

Этот вызов вызывает цепочку уведомлений, которые могут быть трудно отследить, и, конечно, изменения в одном респонденте могут иметь последствия для другого, что, как я подозреваю, объясняет, почему Apple ничего не сделала.

Тем не менее, это нормально в Set, и это только операции Set на OrderedSet, которые работают со сбоями. Это означает, что есть только четыре метода, которые необходимо изменить. Поэтому все, что я сделал, это преобразовал операции 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];
    }]];
}

Жаль, что все остальные, кажется, пропустили этот ответ, определенно кажется лучшим решением.
Джордж

Это решение имеет гораздо лучшую производительность, чем те, которые используют orderSetWithOrderedSet для создания локального набора. Это имеет большие издержки, когда у вас большие наборы данных. Кажется, что более простое решение - это просто реорганизованная версия исходного кода с не показанными методами.
Дэвид Петтигрю

1
Я все еще вижу сбой в addChildren: *** Завершение работы приложения из-за необработанного исключения 'NSInvalidArgumentException', причина: '- [TrackHistory insertTrackpoints: atIndexes:]: нераспознанный селектор, отправленный экземпляру 0x1702b1b20'
Виктор Богдан,

@OwenGodfrey Для более простого решения, где вы реализуете эти методы? Я получаю исключение: [Parent insertObject: inChildrenAtIndex:] нераспознанный селектор отправлен на экземпляр 0x6180000ac480.
Далмацио

ваша переменная "Parent" с большой буквы "P"? Означает ли это, что вы вызываете класс «Parent», или у вас есть переменная экземпляра с именем «Parent»? Если мой класс - Parent, то я реализовал эти методы в нижней части Parent, но вам нужно будет вызывать его для экземпляра, который, скорее всего, будет называться «parent» с нижним регистром «p», поскольку это не методы класса. ,
Оуэн Годфри

10

Документ Apple To Many Relations говорит: вы должны получить доступ к изменяемому набору прокси или упорядоченному набору, используя

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

Изменение этого набора добавит или удалит отношения к вашему управляемому объекту. Доступ к изменяемому упорядоченному набору с помощью метода доступа с помощью [] или. запись неверна и потерпит неудачу.


3
Справедливости ради, в документах также говорится: «или один из автоматически генерируемых методов-мутаторов отношений (см. Динамически генерируемые методы доступа):»
Мэтт

Ладно ладно ... ты прав. Что ж, скажем так, это самый простой способ работы ...
Николас Манзини

9

Получил ту же ошибку, у меня сработало решение @LeeIII (спасибо!). Я предлагаю немного изменить это:

  • используйте категорию target-c для хранения нового метода (поэтому мы не потеряем наш метод, если 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

Хороший момент, чтобы переместить этот код в категорию. Но все же нам нужно охватить фактические добавления / удаления вызовами will / setPrimitiveValue / didChange, как в ответе @Dmitry Makarenko.
Владимир Шутюк

8

Если вы используете mogenerator, то вместо

[parentObject add<Child>sObject:childObject];

просто используйте:

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

Поскольку mogenerator позаботится о дополнительном коде, который в противном случае вам пришлось бы писать, он просто предоставляет доступ к базовому объекту set.
Καrτhικ

Похоже, что только что было исправлено, что означает, что mogenerator будет генерировать исправленные тела ... github.com/dmakarenko/mogenerator/commit/…
combinatorial

1
Я использую, mogeneratorно у меня все еще есть ошибка.
Колас

7

Лично я только что заменил вызовы сгенерированных методов CoreData на прямые вызовы метода, как описано в другом решении @Stephan:

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

Это устраняет необходимость в категориях, которые могут позже вступить в конфликт с решением Apple для сгенерированного кода, когда ошибка будет исправлена.

Это имеет дополнительный плюс того, чтобы быть официальным способом сделать это!


Это приводит к следующей ошибке: '[<CLASS 0x20886d10> valueForUndefinedKey:]: этот класс не совместим со значением ключа для подпунктов ключа.'
jmstone617

Несмотря на то, что меня до сих пор не раздражает, что этого нет в известных проблемах Apple (я открыл радар для явно бесполезного жеста), это решение сработало для меня безупречно.
Скотт Корскадден

Жаль, что я не видел этот ответ ранее; Я использовал ответ с наибольшим количеством голосов, пока недавно не
начал

Почему addObject:называется дважды?
Джейсон Мур

5

Кажется, что если вы связываете родителя с потомком, устанавливая родителя для потомка, а не наоборот, это работает без сбоев.

Так что если вы делаете:

[child setParent:parent]

вместо

[parent setChildObects:child]

Это должно работать, по крайней мере, это работает на iOS 7 и не было никаких проблем с отношениями.


1
Не приносит много пользы, когда обе стороны слишком многим. Тогда нет четких отношений между родителями и детьми.
фатухоку

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;

Эффект состоит в том, что он использует собственный код яблока, и это просто и чисто. Кроме того, набор автоматически добавляется, а все наблюдатели обновляются. Нет проблем.


Это очень мило. Это решает всю проблему и поддерживает порядок. Все еще безумие, что ошибка все еще присутствует. Другое преимущество этого ответа заключается в том, что если вы обновляете свои модели базовых данных, вам не нужно переписывать исправления ошибок. Спасибо!
Йохан С

Смотрите мой другой ответ. Я отслеживал ошибку более подробно. Это все еще самый простой способ, но другой метод является лучшим, потому что он открывает больше возможностей.
Оуэн Годфри

Вот Это Да! В заключение!!! Спасибо! (Опробовал другой код, но получил ошибки, что-то об этом неправильном типе было отправлено в [self didChange: NSKeyValueChangeInsertion valuesAtIndexes: indexSet forKey: ChildrenKey];)
Леонард Паули

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

Эта проблема возникла у меня при переносе проекта с Objective-C на Swift 2 с XCode 7 . Этот проект работал, и по уважительной причине: я использовал 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.

- (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. Фреймворк 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
Итак, если я не могу внедрить другие исправления, что я должен сделать, чтобы это исправить?
Бен Легжеро

2

Я решил эту проблему, установив обратное значение «Нет обратного», я не знаю почему, возможно, есть Apple Bug.введите описание изображения здесь


1

У меня такая же ситуация с элементом под названием «сигналы» вместо «подпункты». Решение с 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 пунктов, поэтому производительность не так важна - тем не менее, пожалуйста, укажите на что-нибудь важное.

Спасибо,

Damien


1

Я нашел этот вопрос, прибегая к поиску сообщения об ошибке, и просто хотел указать, что столкнулся с этой ошибкой немного по-другому (не используя упорядоченные наборы). Это не совсем ответ на заданный вопрос, но я выкладываю его здесь только на тот случай, если он пригодится всем, кто сталкивается с этим вопросом во время поиска.

Я добавил новую версию модели, добавил некоторые отношения к существующим моделям и сам определил методы add * Object в заголовочном файле. Когда я попытался позвонить им, я получил ошибку выше.

Просмотрев мои модели, я понял, что тупо забыл поставить галочку напротив «Отношения со многими».

Поэтому, если вы сталкиваетесь с этим и не используете упорядоченные наборы, дважды проверьте свою модель.


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

Я обнаружил, что использование метода ЛиIII сработало, но при профилировании оказалось, что он был чрезвычайно медленным. Для разбора 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

Роберт,

Я согласен, что ваш ответ будет работать для этого, но имейте в виду, что есть автоматически созданный метод для добавления целого набора значений в отношения. Документация Apple ( как показано здесь в разделе «Отношения ко многим» или здесь в разделе «Пользовательские методы доступа к многим отношениям») реализует их следующим образом:

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

Вы можете легко скомпилировать ваш набор отношений вне основных данных, а затем добавить их все сразу, используя этот метод. Это может быть менее уродливо, чем метод, который вы предложили;)


Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.