РАЗРАБОТКА IPHONE/IPOD TOUCH ПРИЛОЖЕНИЙ: ОБРАТНЫЕ СВЯЗИ В МОДЕЛИ ДАННЫХ COREDATA

Когда возникает необходимость создать в модели отношение «один ко многим»  (такое, как показано на рисунке),
Model Entities

то может показаться, что обратные отношения ставить незачем, как и при отношениях «один к одному» . И действительно, ваше приложение запустится и не будет падать из-за этого, хотя компилятор и выдаст вам Warning’и. Но не все так просто…

Рассмотрим этот случай на примере магазина, в котором содержатся товары. Для этого нам потребуется две сущности: Store и Item. Store содержит отношение items к сущности Item как один ко многим.

Множественное отношение items представляется в классе Store - наследнике класса NSManagedObject как property типа NSSet. Так же для того, чтобы добавлять товары в магазин и удалять их оттуда, нам потребуется объявить accessor-методы, которые можно сгенерировать из модели (находим в модели сущность Store -> правый клик на отношении items -> “Copy Obj-C 2.0 Method Declarations to Clipboard”) . Теперь, когда модель готова, импортировав хэдер класса Store, мы можем использовать эти accessor – методы.  Ниже приведен пример хэдера класса Store:


@class Item;

@interface Store : NSManagedObject {
}

@property (nonatomic, retain) NSSet* items;

@end

@interface Store (CoreDataGeneratedAccessors)
- (void)addItemsObject:(Item *)value;
- (void)removeItemsObject:(Item *)value;
- (void)addItems:(NSSet *)value;
- (void)removeItems:(NSSet *)value;

@end

После добавления новых товаров при помощи метода addItemsObject: и сохранения контекста все впорядке – после рестарта приложения добавленные объекты присутствуют в хранилище. Но вот если удалить какой-нибудь товар методом removeItemsObject: и сохранить контекст, то после рестарта, а может и раньше, можно будет заметить, что этот товар снова появится в наборе items в контексте, хотя ошибку сохранения нам не вернут. Т.е. реально в базе связь не удалилась.

Чтобы этого не происходило, следует в модели создать обратное отношение (Inverse relationship)  «один к одному» (как в данном случае) либо «один ко многим» у сущности Item к сущности Store (назовем его store), и модифицировать класс Item соответствующим образом.
Model with Inverse Relationships
Теперь удаление товаров будет происходить корректно.

Почему так получилось? Вероятно, при сохранении в хранилище после удаления эти обратные отношения используются на уровне sql-запросов.

P. S. В статье рассматривается случай, когда используется SQLite-хранилище CoreData.

Комментарии

Alex§

в 11:35, 05.08.2009

Интересное наблюдение. В какой программке составлялись диаграммы, если не секрет?

Павел Тайкало

в 13:45, 05.08.2009

И совсем не секрет
Xcode 3.1.2

Илья Кулаков

в 12:28, 11.08.2009

Вообще в своем гайде Apple не рекомендует использовать односторонние relationship. Там же приводят несколько возможных путей решения задачи.

А вы пробовали смотреть deletedObjects у контекста? Присутствует ли там удаленные объект?

Евгений Дудник

в 12:46, 11.08.2009

Вы спрашиваете об удалении объекта store и каскадном удалении item или только объекта item (если использовать пример в статье)?

Илья Кулаков

в 20:35, 11.08.2009

Мда, глупость какую-то сморозил. Почудилось будто вы Item удаляете из контекста а не из Store.

Вообще вот вырезка из Core Data Programmin Guide:
If you create a model with unidirectional relationships (relationships where you have specified no inverse), your object graph may end up in an inconsistent state.

Евгений Дудник

в 21:32, 11.08.2009

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

Оставить комментарий