Кажется, эта ошибка появлялась на ТАК много раз. Наиболее очевидным ответом на эту проблему является RestKit 0.20 - CoreData: ошибка: не удалось вызвать назначенный инициализатор в классе NSManagedObject
Однако опубликованное решение не работает для меня. Но начнем с самого начала:
Я хочу загрузить файл на сервер. Я хочу проанализировать ответ, чтобы убедиться, что он был успешно загружен. Если нет, я попробую позже. В моей модели для этой цели есть логическая переменная NSNumber *sentToServer
(в CoreData BOOL
сохраняется как NSNumber
).
Поскольку я не знаю, будет ли отправка успешной, я всегда сначала сохраняю объект в моей модели CoreData, прежде чем отправлять его через RestKit.
Когда запрос POST успешен, сервер возвращает строку JSON, подобную этой:
{"id":14,"created":true}
Обратите внимание, что, конечно, идентификатор еще не установлен в модели CoreData, поэтому и id
, и created
являются новой информацией для моей модели.
Вот моя модель:
@interface Recording : NSManagedObject
@property (nonatomic, retain) NSString * audioFilePath;
@property (nonatomic, retain) NSDate * createdAt;
@property (nonatomic, retain) NSString * deviceId;
@property (nonatomic, retain) NSString * deviceType;
@property (nonatomic, retain) NSNumber * recordingId;
@property (nonatomic, retain) NSNumber * sentToServer;
@end
И вот как я это настроил:
NSURL *modelURL = [NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:@"HTRecording" ofType:@"momd"]];
NSManagedObjectModel *managedObjectModel = [[[NSManagedObjectModel alloc] initWithContentsOfURL:modelURL] mutableCopy];
RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"AudioModel.sqlite"];
NSError *error = nil;
NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
if (![managedObjectStore addSQLitePersistentStoreAtPath:storePath
fromSeedDatabaseAtPath: nil
withConfiguration:nil
options:options
error:&error]) {
NSAssert(NO, @"Managed object store failed to create persistent store coordinator: %@", error);
}
[managedObjectStore createManagedObjectContexts];
// Configure MagicalRecord to use RestKit's Core Data stack
[NSPersistentStoreCoordinator MR_setDefaultStoreCoordinator:managedObjectStore.persistentStoreCoordinator];
[NSManagedObjectContext MR_setRootSavingContext:managedObjectStore.persistentStoreManagedObjectContext];
[NSManagedObjectContext MR_setDefaultContext:managedObjectStore.mainQueueManagedObjectContext];
NSString *databaseUrl = @"http://localhost:3000";
RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:[NSURL URLWithString:databaseUrl]];
objectManager.managedObjectStore = managedObjectStore;
objectManager.requestSerializationMIMEType = RKMIMETypeJSON;
// Setup mapping
RKEntityMapping* recordingRequestMapping = [RKEntityMapping mappingForEntityForName:@"Recording" inManagedObjectStore:managedObjectStore];
[recordingRequestMapping addAttributeMappingsFromDictionary:@{
@"device_id": @"deviceId",
@"device_type": @"deviceType" }];
RKRequestDescriptor *requestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[recordingRequestMapping inverseMapping]
objectClass:[Recording class]
rootKeyPath:@"recording"];
[objectManager addRequestDescriptor:requestDescriptor];
RKEntityMapping* recordingResponseMapping = [RKEntityMapping mappingForEntityForName:@"Recording" inManagedObjectStore:managedObjectStore];
[recordingResponseMapping addAttributeMappingsFromDictionary:@{
@"created": @"sentToServer",
@"id": @"recordingId"}];
recordingResponseMapping.identificationAttributes = @[@"recordingId"];
RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:recordingResponseMapping
pathPattern:@"/audio.json"
keyPath:nil
statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[objectManager addResponseDescriptor:responseDescriptor];
Во-первых, это удивительно длинный установочный код для чего-то, что должно быть настолько распространенным (просто запустите RestKit и MagicalRecord). Но я отвлекся ...
Весь процесс загрузки работает нормально. Отображения запросов хорошие, все хранится на сервере, включая вложения. Обратите внимание, что сопоставления запросов не содержат файлов, поскольку они отправляются с помощью методов multipart post. Опять же, эта часть работает нормально. Вот код для загрузки файлов:
RKObjectManager *manager = [RKObjectManager sharedManager];
NSMutableURLRequest *request;
request = [manager multipartFormRequestWithObject:recording
method:RKRequestMethodPOST
path:@"audio.json"
parameters:nil
constructingBodyWithBlock:^(id<AFMultipartFormData> formData) {
[formData appendPartWithFileData:[[NSFileManager defaultManager] contentsAtPath:recording.audioFilePath]
name:@"recording[audio]"
fileName:[recording.audioFilePath lastPathComponent]
mimeType:@"audio/m4a"];
}];
RKObjectRequestOperation *operation = [manager objectRequestOperationWithRequest:request
success:^(RKObjectRequestOperation *op, RKMappingResult *mapping) {
NSLog(@"successfully saved file on the server");
}
failure:^(RKObjectRequestOperation *op, NSError *error){
NSLog(@"some weird error");
}];
[manager enqueueObjectRequestOperation:operation];
Отображение реакции на объект становится затруднительным. Без соответствующего сопоставления ответов выходные данные журнала содержат ошибку, которая не может обработать ответ (сюрприз). При совпадающем сопоставлении ответов приложение аварийно завершает работу. Вот трассировка стека:
#11 0x2f6482d6 in -[NSObject(NSKeyValueCoding) valueForKeyPath:] ()
#12 0x00113b9a in -[RKMappingOperation shouldSetValue:forKeyPath:usingMapping:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMappingOperation.m:436
#13 0x00114e40 in -[RKMappingOperation applyAttributeMapping:withValue:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMappingOperation.m:536
#14 0x00115fd4 in -[RKMappingOperation applyAttributeMappings:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMappingOperation.m:577
#15 0x0011c988 in -[RKMappingOperation main] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMappingOperation.m:948
#16 0x2f655d0a in -[__NSOperationInternal _start:] ()
#17 0x0010cd5a in -[RKMapperOperation mapRepresentation:toObject:atKeyPath:usingMapping:metadata:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMapperOperation.m:256
#18 0x0010b6aa in -[RKMapperOperation mapRepresentation:atKeyPath:usingMapping:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMapperOperation.m:176
#19 0x0010ddc8 in -[RKMapperOperation mapRepresentationOrRepresentations:atKeyPath:usingMapping:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMapperOperation.m:314
#20 0x0010e552 in -[RKMapperOperation mapSourceRepresentationWithMappingsDictionary:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMapperOperation.m:359
#21 0x0010ee72 in -[RKMapperOperation main] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/ObjectMapping/RKMapperOperation.m:398
#22 0x2f655d0a in -[__NSOperationInternal _start:] ()
#23 0x00100e70 in -[RKObjectResponseMapperOperation performMappingWithObject:error:] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/Network/RKResponseMapperOperation.m:345
#24 0x000ff4e6 in -[RKResponseMapperOperation main] at /Users/earthnail/development/RestkitFileUpload/Pods/RestKit/Code/Network/RKResponseMapperOperation.m:297
#25 0x2f655d0a in -[__NSOperationInternal _start:] ()
Строка, в которой отладчик останавливается с SIGABRT
в [RKMappingOperation shouldSetValue:forKeyPath:usingMapping:]
:
id currentValue = [self.destinationObject valueForKeyPath:keyPath];
Обратите внимание, что когда я устанавливаю точку останова, как предложено в https://stackoverflow.com/a/13734348/487556, эта точка останова никогда не достигается. Это означает, что нет проблем с путем отображения ответа.
Я могу представить несколько вещей, которые здесь идут не так:
- RestKit предполагает, что сущности CoreData еще нет, и хочет ее создать. Это привело бы к дублированию сущностей (что было бы облом!), Но все равно не должно произойти сбоя, поскольку все поля модели являются необязательными.
- RestKit пытается найти сущность CoreData на основе идентификатора (поскольку это переменная идентификатора), но, конечно, идентификатор еще не установлен.
- Что-то другое...
Какие-либо предложения?
objectManager
? - person Wain   schedule 16.08.2013