Поведение CoreData NSSet-Like для NSManagedObjects с теми же значениями

У меня есть чат-приложение с такой моделью данных.

User <--> Conversation <-->> Message

Моя проблема сейчас: иногда, если я получаю старые сообщения из резервной копии, у меня дважды появляются сообщения в моей модели данных. Я хотел бы иметь класс NSSet-Like, который распознает, имеет ли Message-Object точно такие же значения в своих свойствах. Я читал, что нельзя переопределять методы -hash и -isEqual:, поэтому не знаю, как это сделать. Любая идея? Вот некоторый код...

+(void)addMessages:(NSSet<JSQMessage *> *)messages toConversation:(Conversation *)conversation
{
    DataManager * dataManager = [DataManager dataManager];
    NSMutableSet * storeSet = [NSMutableSet setWithCapacity:messages.count];

for (JSQMessage * jsqMessage in messages) {
    Message * message = [NSEntityDescription insertNewObjectForEntityForName:CDEntityNameMessage inManagedObjectContext:[dataManager managedObjectContext]];
    message.senderId = jsqMessage.senderId;
    message.senderDisplayName = jsqMessage.senderDisplayName;
    message.text = jsqMessage.text;
    message.date = jsqMessage.date;
    [storeSet addObject:message];
}
[conversation addMessages:storeSet];

NSError *error;
if (![[dataManager managedObjectContext] save:&error]) {
    NSLog(@"Something went wrong: %@", [error localizedDescription]);
} else {
    //Saved successfull
}
}

И метод Conversation -addMessages: автоматически генерируется из Xcode/CoreData.

- (void)addMessages:(NSSet<Message *> *)values;

person Stephan Boner    schedule 26.07.2016    source источник


Ответы (2)


Один из способов сделать это — добавить уникальные ограничения для вашего объекта для одного или нескольких атрибутов. Но эта функция была добавлена ​​из iOS 9. Вот ссылка на видео WWDC, объясняющее ее: https://developer.apple.com/videos/play/wwdc2015/220/

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

Ваш хэш-метод может выглядеть примерно так:

- (NSUInteger)hash 
{
    NSInteger hashResult = 0;
    for (NSObject *ob in self)
    {
        hashResult ^= [ob hash];
    }
}

Это не лучшая реализация хеш-функции. Посмотрите этот ответ: https://stackoverflow.com/a/5915445/2696922

Для метода isEqual это может выглядеть примерно так:

- (BOOL)isEqual:(id)object 
{
    if (self == object) 
    {
        return YES;
    }

    if (object == nil || ![object isKindOfClass:[JSQMessage class]]) 
    {
        return NO;
    }

    JSQMessage *jsqMessage = (JSQMessage*)object;

    //You can have more parameters here based on your business logic
    if (self.message != jsqMessage.message && self.date != jsqMessage.date)
    {
        return NO;
    }
}
person Kunal Shrivastava    schedule 26.07.2016
comment
Спасибо! Вы уверены, что я могу без проблем переопределить хэш-функцию? - person Stephan Boner; 27.07.2016
comment
Если я переопределю isEqual, мое приложение выйдет из строя. Класс «Сообщение» для объекта «Сообщение» имеет недопустимое переопределение NSManagedObject -isEqual: - person Stephan Boner; 27.07.2016
comment
Мне жаль, что я ошибся, сказав, что вы можете переопределить hash и isEqual. Что вы можете сделать, так это создать другой метод с другим именем и переместить туда свою логику isEqual. Вы можете игнорировать метод хеширования, так как вы просто хотите проверить, имеют ли два объекта одинаковые данные или нет. Посмотрите, работает ли это. - person Kunal Shrivastava; 27.07.2016

Сейчас я проверяю вручную, есть ли в моем MOC объект с такими же атрибутами. Если он есть, я пропускаю создание. Я знаю, это немного неэффективно, но с моим ожидаемым количеством сообщений это не должно быть проблемой.

NSFetchRequest * fr = [NSFetchRequest fetchRequestWithEntityName:CDEntityNameMessage];
  [fr setPredicate:[NSPredicate predicateWithFormat:@"text == %@ AND date == %@ AND conversation.user.objectId == %@", message.text, message.date, chatpartner.objectId]];
  NSArray * results = [[self managedObjectContext] executeFetchRequest:fr error:nil];
  if (results && results.count > 0) {
     continue;
  }
person Stephan Boner    schedule 28.07.2016