#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.