使用 RestKit 导致核心数据崩溃的多个 RKResponseDescriptor

Posted

技术标签:

【中文标题】使用 RestKit 导致核心数据崩溃的多个 RKResponseDescriptor【英文标题】:Multiple RKResponseDescriptors causing crash with Core Data with RestKit 【发布时间】:2013-04-22 00:16:47 【问题描述】:

我在单例中为每个服务调用创建我的 RestKit 映射,例如:

- (void)setupMapping

RKObjectManager *objectManager = [RKObjectManager sharedManager];
RKEntityMapping *challengesMapping  = [RKEntityMapping    mappingForEntityForName:@"Challenge" inManagedObjectStore:[objectManager managedObjectStore]];

[challengesMapping addAttributeMappingsFromDictionary:@
 @"uuid": @"uuid",
 @"title": @"title",
 @"description": @"challengeDescription",
 @"icon": @"icon",
 @"active_from": @"activeFrom",
 @"active_to": @"activeTo",
 @"trigger": @"trigger",
 @"show_in_feed": @"showInFeed",
 @"points": @"points",
 @"trigger": @"trigger",
 @"type": @"type",
 @"min_level": @"minLevel"
 ];
 challengesMapping.identificationAttributes = @[ @"uuid" ];

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:challengesMapping
                                                                                   pathPattern:CHALLENGE_PATH
                                                                                       keyPath:@"challenges"
                                                                                   statusCodes:[NSIndexSet indexSetWithIndex:SUCCESS]];
[objectManager addResponseDescriptor:responseDescriptor];

RKObjectMapping *sessionMapping = [RKObjectMapping mappingForClass:[TimeStamp class]];
[sessionMapping addAttributeMappingsFromArray:@[@"ts"]];

[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:sessionMapping
                                                                             pathPattern:CHALLENGE_PATH
                                                                                 keyPath:nil
                                                                            statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];


- (void)setupMapping

   RKObjectManager *objectManager = [RKObjectManager sharedManager];
RKEntityMapping *festivalsMapping  = [RKEntityMapping mappingForEntityForName:@"Festival" inManagedObjectStore:[objectManager managedObjectStore]];

[festivalsMapping addAttributeMappingsFromDictionary:@
 @"uuid": @"uuid",
 @"festival": @"festivalDescription",
 @"start_ts": @"start_ts",
 @"end_ts": @"end_ts",
 @"title": @"title"
 ];
festivalsMapping.identificationAttributes = @[ @"uuid" ];

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:festivalsMapping
                                                                                   pathPattern:GET_FESTIVALS_PATH
                                                                                       keyPath:@"festivals"
                                                                                   statusCodes:[NSIndexSet indexSetWithIndex:SUCCESS]];
[objectManager addResponseDescriptor:responseDescriptor];
RKObjectMapping* sessionMapping = [RKObjectMapping mappingForClass:[TimeStamp class]];
[sessionMapping addAttributeMappingsFromArray:@[@"ts"]];

[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:sessionMapping
                                                                             pathPattern:GET_FESTIVALS_PATH
                                                                                 keyPath:nil
                                                                            statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]];

第一个映射(挑战)的第一个服务器调用工作正常,但是当我调用第二个(节日映射)时,我收到错误:“无法从此 NSManagedObjectContext 的协调器访问对象的持久存储”。我知道这可能是 Core Data 中的线程问题,但我在代码中找不到原因。

我在控制台中得到以下信息:

(lldb) po $r0 错误:无法实现结构:无法读取 r0(实现) 执行时出错,无法 PrepareToExecuteJITExpression (lldb) 寄存器读取 通用寄存器: r4 = 0x00000000 r5 = 0x00066e95 MyAppmain + 1 at main.m:14 r6 = 0x00000000 r7 = 0x2fd9ccf8 r8 = 0x2fd9cd10 r10 = 0x00000000 r11 = 0x00000000 r12 = 0x00000148 sp = 0x2fd9ccd4 lr = 0x00066f09 MyAppmain + 117 在 main.m:16 pc = 0x00066f09 MyApp`main + 117 at main.m:16 cpsr = 0x00000010 5 个寄存器不可用。

编辑

这是服务/映射类之一的完整示例。我之前看到过类似的模式,即使用 GCD 单例。我也不认为 TimeStamp 根据下面的评论是重复的,因为 pathPatterns 是不同的。正确的?我确实尝试删除它们,但同样的问题。这是预期的,因为它们没有得到 Core Data 的支持

#import "ChallengeService.h"

static ChallengeService __strong *defaultService = nil;

#define CHALLENGE_PATH @"/api/challenges"

@implementation ChallengeService

+ (ChallengeService *)defaultService

static dispatch_once_t pred;
dispatch_once(&pred, ^
    defaultService = [[self alloc] initWithPath:CHALLENGE_PATH];
    [defaultService setupMapping];
);

return defaultService;


- (void)setupMapping

RKObjectManager *objectManager = [RKObjectManager sharedManager];
RKEntityMapping *challengesMapping  = [RKEntityMapping     mappingForEntityForName:@"Challenge" inManagedObjectStore:[objectManager managedObjectStore]];

[challengesMapping addAttributeMappingsFromDictionary:@
 @"uuid": @"uuid",
 @"title": @"title",
 @"description": @"challengeDescription",
 @"icon": @"icon",
 @"active_from": @"activeFrom",
 @"active_to": @"activeTo",
 @"trigger": @"trigger",
 @"show_in_feed": @"showInFeed",
 @"points": @"points",
 @"trigger": @"trigger",
 @"type": @"type",
 @"min_level": @"minLevel"
 ];
 challengesMapping.identificationAttributes = @[ @"uuid" ];

RKResponseDescriptor *responseDescriptor = [RKResponseDescriptor    responseDescriptorWithMapping:challengesMapping
                                                                                      pathPattern:CHALLENGE_PATH
                                                                                           keyPath:@"challenges"
                                                                                   statusCodes:[NSIndexSet indexSetWithIndex:SUCCESS]];
[objectManager addResponseDescriptor:responseDescriptor];

RKObjectMapping *sessionMapping = [RKObjectMapping mappingForClass:[TimeStamp class]];
[sessionMapping addAttributeMappingsFromArray:@[@"ts"]];

[objectManager addResponseDescriptor:[RKResponseDescriptor responseDescriptorWithMapping:sessionMapping
                                                                             pathPattern:CHALLENGE_PATH
                                                                                 keyPath:nil
                                                                            statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)]]; 

- (void)getChallengesFromDate:(NSDate *)date
                onSuccess:(DidSucceedBlock)successBlock
                  onError:(DidFailWithErrorBlock)failBlock
    
[defaultService getWithData:nil
                   fromDate:date
                     onLoad:^(id object) 
                         successBlock(object);
                      onError:^(NSError *error) 
    failBlock(error);
];


【问题讨论】:

【参考方案1】:

首先,您不能多次将同一个映射添加到同一个对象管理器。考虑使用多个不同的对象管理器(每组请求一个,即每个单例一个)或预先定义公共映射。

然后,在您的问题中添加有关何时创建和配置这些单例的详细信息。它们中的任何一个是在后台线程上运行还是在应用程序启动时预先完成所有配置。如果您知道应用在每次使用时都会使用所有配置,那么请提前做好所有事情,因为构建配置的成本很低。

您需要小心defaultService,因为目前您会将访问器方法与静态变量混淆。在方法内部定义静态变量并将其命名为_defaultService。然后,您应该将 setup 方法移动到 initWithPath,因为它是初始化的一部分。确保您正确调用了访问器方法,并且您没有从后台线程调用它(至少是第一次,并且可能每次)。

【讨论】:

以上是关于使用 RestKit 导致核心数据崩溃的多个 RKResponseDescriptor的主要内容,如果未能解决你的问题,请参考以下文章

将输出映射到 Restkit 中的核心数据时崩溃

RestKit 数据映射导致应用程序崩溃

Restkit Core Data 映射崩溃应用程序

快速连续发出多个请求时 RestKit 崩溃

多个对象没有得到保存的核心数据restkit

在 RestKit 将其保存在核心数据中之前拦截成功的响应(可能的回调)