【发布时间】:2013-08-17 23:56:08
【问题描述】:
这个错误似乎已经出现过很多次了。这个问题最突出的答案似乎是 RestKit 0.20 — CoreData: error: Failed to call designated initializer on NSManagedObject class
但是,发布的解决方案对我不起作用。但是让我们从头开始:
我想将文件上传到服务器。我想解析响应以确保它已成功上传。如果没有,我稍后再试。为此,我的模型有一个名为 NSNumber *sentToServer 的布尔变量(在 CoreData 中,BOOL 保存为 NSNumber)。
因为我不知道发送是否会成功,所以我总是先将实体保存到我的 CoreData 模型中,然后再通过 RestKit 发送。
当 POST 请求成功时,服务器返回一个 JSON 字符串,如下所示:
{"id":14,"created":true}
请注意,当然,尚未在 CoreData 模型上设置 ID,因此 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),这是一个令人惊讶的冗长设置代码。但我离题了...
整个上传过程运行良好。请求映射很好,所有内容都存储在服务器上,包括附件。请注意,请求映射不包含文件,因为它们是通过多部分 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:] ()
调试器在[RKMappingOperation shouldSetValue:forKeyPath:usingMapping:] 中以SIGABRT 停止的行是:
id currentValue = [self.destinationObject valueForKeyPath:keyPath];
请注意,当我按照https://stackoverflow.com/a/13734348/487556 中的建议设置断点时,永远不会到达该断点。这意味着响应映射的路径没有问题。
我可以想象这里有几件事会出错:
- RestKit 假设还没有 CoreData 实体并想要创建一个。这会导致重复的实体(这会很糟糕!),但它仍然不应该崩溃,因为模型的所有字段都是可选。
- RestKit 尝试根据 ID 查找 CoreData 实体(因为这是标识符变量),但当然 ID 尚未设置。
- 别的东西...
有什么建议吗?
【问题讨论】:
-
Reskit 无法通过主键匹配对象,因为它们在本地没有主键。您应该向模型添加另一个属性 - 全局唯一标识符,以用作标识属性,而不是使用 recordingId。此外,删除 sentToServer 属性,并在网络连接可用时获取带有 recordingId == 0 的对象以进行同步。
-
您的实际发帖情况如何?您如何使用
objectManager? -
serrrgi,你在 sentToServer 属性上是对的。我现在实际上正在使用它作为一种解决方法。如果我让服务器只响应一个 HTTP 标头,Restkit 就没有什么可映射的,因此它成功了。然后,在 RequestOperation 的成功块中,我可以手动将 sentToServer 设置为 true。我同意对于我想做的响应映射,我不需要 sentToServer 属性!不过,我不明白为什么 Restkit 无法匹配对象。毕竟在成功块中,我可以手动访问我发送的对象,不需要任何ID,所以RK应该也可以这样做
-
Wain,我更新了帖子以包含发布代码。
标签: ios core-data restkit magicalrecord