primaryKeyAttribute не работает Restkit / Core Data

#iphone #ios #core-data #restkit

#iPhone #iOS #core-data #restkit

Вопрос:

Я только что установил фреймворк restkit 0.9.3 и последовал примеру доски обсуждений. Ну, все просто отлично работало, однако, когда я попытался использовать Core Data, мой пользовательский класс NSManagedObject дублируется даже после объявления его primaryKeyAttribute (userId). Например, когда я отправляю запрос на вход на свой веб-сервер, я возвращаюсь {"user":{"id":1, "username":"teste", ...}} .. но, похоже, он создает новую строку каждый раз, когда вызывает ObjectLoader:didLoadObjects .

Таблица пользователя:

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

Пример кода:

~ AppDelegate.m завершил запуск

 RKManagedObjectMapping* userMapping = [RKManagedObjectMapping mappingForClass:[User class]];    
userMapping.primaryKeyAttribute = @"userID";
userMapping.setDefaultValueForMissingAttributes = YES; // clear out any missing attributes (token on logout)
[userMapping mapKeyPathsToAttributes:
     @"id", @"userID",
     @"email", @"email",
     @"username", @"username",
     @"password", @"password",
     nil];

[objectManager.mappingProvider registerMapping:userMapping withRootKeyPath:@"user"];
  

~ User.m loginWithDelegate

 - (void)loginWithDelegate:(NSObject<UserAuthenticationDelegate>*)delegate {
    _delegate = delegate;   
    [[RKObjectManager sharedManager] postObject:self delegate:self block:^(RKObjectLoader* loader) {
        loader.resourcePath = @"/login";
        loader.serializationMapping = [RKObjectMapping serializationMappingWithBlock:^(RKObjectMapping* mapping) {
            [mapping mapAttributes:@"username", @"password", nil];            
        }];
    }];
}
  

~ User.m сделал loadobjects (RKObjectLoaderDelegate)

 - (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObjects:(NSArray *)objects {

    if ([objectLoader wasSentToResourcePath:@"/login"]) {
        [self loginWasSuccessful];
    }

    NSLog(@"number of user rows: %i", [User findAll].count);
}
  

Что я делаю не так?

Комментарии:

1. Может быть, это должен быть вызов — (NSManagedObject*)findOrCreateInstanceOfEntity: (NSEntityDescription*)entity с primarykeyattribute: (NSString*)primaryKeyAttribute andValue:(id)primaryKeyValue; ?

Ответ №1:

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

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

Если вы вернете nil, он подумает, что этого объекта нет в вашем кэше, и добавит дубликат. Попробуйте реализовать этот метод:

   (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
              withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
                                value:(id)primaryKeyValue
               inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext
  

Например:

   (NSManagedObject *)findInstanceOfEntity:(NSEntityDescription *)entity
              withPrimaryKeyAttribute:(NSString *)primaryKeyAttribute
                                value:(id)primaryKeyValue
               inManagedObjectContext:(NSManagedObjectContext *)managedObjectContext {

    NSFetchRequest* request = [[NSFetchRequest alloc] init];
    [request setEntity: entity];
    [request setFetchLimit: 1];
    [request setPredicate:[NSPredicate predicateWithFormat:@"%K = %@", primaryKeyAttribute, primaryKeyValue]];

    NSArray *results = [NSManagedObject executeFetchRequest:request inContext: managedObjectContext];
    if ([results count] == 0)
    {
     return nil;
    }
    return [results objectAtIndex:0];
}
  

Ответ №2:

Я обнаружил проблему, связанную с targetObject (RKObjectLoader)

 /**
 * The target object to map results back onto. If nil, a new object instance
 * for the appropriate mapping will be created. If not nil, the results will
 * be used to update the targetObject's attributes and relationships.
 */
  

Поэтому, когда я устанавливаю его в nil postObject, вызывает findOrCreateInstanceOfEntity:с primarykeyattribute:andValue

 - (void)loginWithDelegate:(NSObject<UserAuthenticationDelegate>*)delegate {
    _delegate = delegate;   
    [[RKObjectManager sharedManager] postObject:self delegate:self block:^(RKObjectLoader* loader) {
        loader.resourcePath = @"/login";
        loader.targetObject = nil;
        loader.serializationMapping = [RKObjectMapping serializationMappingWithBlock:^(RKObjectMapping* mapping) {
            [mapping mapAttributes:@"username", @"password", nil];            
        }];
    }];
}
  

Комментарии:

1. Итак, для чего установлен targetObject, если вы не установили для него значение nil, из-за которого post создает дубликат объекта? Я бы подумал, что если бы targetObject был установлен на что угодно, он был бы установлен на объект, который будет обновляться в первую очередь…

Ответ №3:

Начиная с последней версии RestKit (0.23.2), вы можете определить первичный ключ следующим образом:

 [_mapping addAttributeMappingsFromDictionary:@{ @"id" : @"objectId", @"name" : @"name" }];
[_mapping setIdentificationAttributes:@[ @"objectId" ]];
  

В то время как ObjectId — это ваш первичный ключ к объекту core data.