Исключение, генерируемое в сгенерированных средствах доступа 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.