【问题标题】:RestKit - 'Cannot add object with entity 'entityName' to cache for entity of 'entityName''RestKit - '无法添加具有实体'entityName'的对象以缓存'entityName'实体'
【发布时间】:2016-02-07 20:42:12
【问题描述】:

我在 uitabbarcontroller 中有带有三个视图控制器的 iPad 应用程序。在每个视图控制器中,我调用不同的 Web 服务并使用 RestKit 将它们映射到核心数据实体,然后使用 nsfetchedresultscontroller 在 uitableview 中显示数据。有关我的实现的更多信息是here

当我第一次加载数据时,数据被正确加载并映射到每个视图控制器上,但在一个视图控制器中,当我在视图控制器 3 中加载数据后尝试在视图控制器 2 中重新加载数据时,应用程序因以下错误而崩溃:

2015-11-07 00:17:45.205 PharmacyStockTake[193:3854] T restkit.network:RKResponseMapperOperation.m:504 将 HTTP 响应映射到 nil 目标对象... 2015-11-07 00:17:45.206 PharmacyStockTake[193:3854] 我 restkit.core_data:RKInMemoryManagedObjectCache.m:94 通过属性“stockTakeLocId”缓存实体“RackStockTakeStatus”的实例 2015-11-07 00:17:45.213 PharmacyStockTake[193:3854] *-[RKEntityByAttributeCache addObjects:completion:]、/Users/saif/Documents/iDev/ComPrjs/PharmacyStockTake/Pods/RestKit/Code 中的断言失败/CoreData/RKEntityByAttributeCache.m:333 2015-11-07 00:17:45.214 PharmacyStockTake[193:3854] * 由于未捕获的异常“NSInternalInconsistencyException”而终止应用程序,原因:“无法添加具有实体“RackStockTakeStatus”的对象以缓存“RackStockTakeStatus”实体' *** 首先抛出调用堆栈: (0x2201585b 0x33a5adff 0x22015731 0x22da6ddb 0x14b67b 0x21d9034b 0x21d9022d 0x14b175 0x15a9e1 0x21d9034b 0x21d9022d 0x15a71d 0x161327 0x17bfb9 0x17a769 0x220091e9 0x21f8bbdb 0x17a65b 0x17c21b 0x17c9cd 0x17d271 0x22d283cf 0x1c3925 0x21d9034b 0x422d03 0x42c4fb 0x21d90247 0x1c26bb 0x1c072d 0x22d283cf 0x22dd682d 0x42d61b 0x425f53 0x42eb0f 0x42e961 0x34314e0d 0x343149fc) libc++abi.dylib:以 NSException 类型的未捕获异常终止

这是我的代码:

在 AppDelegate 中初始化 RestKit:

RKObjectManager *objectManager = [RKObjectManager managerWithBaseURL:baseURL];
    
    //[RKObjectManager setSharedManager:objectManager];
    [RKObjectManager setSharedManager:objectManager];
    
    
    // Initialize managed object model from bundle
    NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
    
    // Initialize managed object store
    RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
    objectManager.managedObjectStore = managedObjectStore;
    
    // Complete Core Data stack initialization
    [managedObjectStore createPersistentStoreCoordinator];
    NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"StockTakeDB.sqlite"];
    NSString *seedPath = [[NSBundle mainBundle] pathForResource:@"StoreItemsDB" ofType:@"sqlite"];
    NSError *error;
    NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
    NSAssert(persistentStore, @"Failed to add persistent store with error: %@", error);
    
    // Create the managed object contexts
    [managedObjectStore createManagedObjectContexts];
    
    // Configure a managed object cache to ensure we do not create duplicate objects
    managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];

View-Controller-2 中的 RestKit 初始化和配置代码:

-(void)initTakeStockRestKit
{
    takeStockLocationWithStatusRequestPath = @"/stocktake/stocktake/1/usr/1/locwithstatus";
    RKObjectManager *objectManager = [RKObjectManager sharedManager];
    
    [[NSURLCache sharedURLCache] removeAllCachedResponses];

    // Initialize managed object model from bundle
    NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
    
    // Initialize managed object store
    RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
    objectManager.managedObjectStore = managedObjectStore;
    
    // Complete Core Data stack initialization
    [managedObjectStore createPersistentStoreCoordinator];
    NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"StockTakeDB.sqlite"];
    NSString *seedPath = [[NSBundle mainBundle] pathForResource:@"StoreItemsDB" ofType:@"sqlite"];
    NSError *error;
    NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
    NSAssert(persistentStore, @"Failed to add persistent store with error: %@", error);
    
    // Create the managed object contexts
    [managedObjectStore createManagedObjectContexts];
    
    // Configure a managed object cache to ensure we do not create duplicate objects
    managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    
    [objectManager addFetchRequestBlock:^NSFetchRequest *(NSURL *URL) {
        RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:takeStockLocationWithStatusRequestPath];
        
        NSDictionary *argsDict = nil;
        BOOL match = [pathMatcher matchesPath:[URL relativePath] tokenizeQueryStrings:NO parsedArguments:&argsDict];
        
        if (match) {
            NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"RackStockTakeStatus"];
            return fetchRequest;
        }
        return nil;
    }];

    RKEntityMapping *rackStockTakeStatusListMapping = [RKEntityMapping mappingForEntityForName:@"RackStockTakeStatus" inManagedObjectStore:managedObjectStore];
    rackStockTakeStatusListMapping.identificationAttributes = @[@"stockTakeLocId"];
    
    [rackStockTakeStatusListMapping addAttributeMappingsFromDictionary:
     @{
       @"stockTakeLocId" : @"stockTakeLocId",
       @"stockTakeUuid" : @"stockTakeUuid",
       @"locId" : @"locId",
       @"locName" : @"locName",
       @"status" : @"status",
       @"stockTakeByUser" : @"stockTakeByUser",
       @"stockTakeByUserId" : @"stockTakeByUserId",
       @"beginTime" : @"beginTime",
       @"percentCompleted" : @"percentCompleted"
       }
     ];
    
    RKResponseDescriptor *rackStockTakeStatusListResponseDescriptor =
    [RKResponseDescriptor responseDescriptorWithMapping:rackStockTakeStatusListMapping
                                                 method:RKRequestMethodGET
                                            pathPattern:@"/stocktake/stocktake/:id/usr/:id/locwithstatus"
                                                keyPath:nil
                                            statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)
     ];
    
    [objectManager addResponseDescriptor:rackStockTakeStatusListResponseDescriptor];
}

在 View-Controller-2 上加载数据:

NSString *requestPath = [NSString stringWithFormat:@"/stocktake/stocktake/%@/usr/1/locwithstatus",[defaults objectForKey:@"loggedInUserSelectedStoreId"]];

[[RKObjectManager sharedManager]
     getObjectsAtPath:requestPath
     parameters:nil
     success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
          NSManagedObjectContext *context = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
    NSFetchRequest *request = [[NSFetchRequest alloc] init];
    NSEntityDescription *entity = [NSEntityDescription entityForName:@"RackStockTakeStatus" inManagedObjectContext:context];
   [request setEntity:entity];
    NSError *error;
    [context executeFetchRequest:request error:&error];
          NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {
    }
         [self.tableView reloadData];
         NSLog(@"requestDataItemsForStore - Mapping Success");
     }
     failure: ^(RKObjectRequestOperation *operation, NSError *error) {
         RKLogError(@"Load failed with error: %@", error);
         NSLog(@"requestDataItemsForStore - Loading Failed");
     }
     ];

View-Controller-3 中的 RestKit 初始化和配置代码:

RKObjectManager *objectManager = [RKObjectManager sharedManager];
    
    // Initialize managed object model from bundle
    NSManagedObjectModel *managedObjectModel = [NSManagedObjectModel mergedModelFromBundles:nil];
    
    // Initialize managed object store
    RKManagedObjectStore *managedObjectStore = [[RKManagedObjectStore alloc] initWithManagedObjectModel:managedObjectModel];
    objectManager.managedObjectStore = managedObjectStore;
    
    // Complete Core Data stack initialization
    [managedObjectStore createPersistentStoreCoordinator];
    NSString *storePath = [RKApplicationDataDirectory() stringByAppendingPathComponent:@"StockTakeDB.sqlite"];
    NSString *seedPath = [[NSBundle mainBundle] pathForResource:@"StoreItemsDB" ofType:@"sqlite"];
    NSError *error;
    NSPersistentStore *persistentStore = [managedObjectStore addSQLitePersistentStoreAtPath:storePath fromSeedDatabaseAtPath:seedPath withConfiguration:nil options:nil error:&error];
    NSAssert(persistentStore, @"Failed to add persistent store with error: %@", error);
    
    // Create the managed object contexts
    [managedObjectStore createManagedObjectContexts];
    
    // Configure a managed object cache to ensure we do not create duplicate objects
    managedObjectStore.managedObjectCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
    
    [objectManager addFetchRequestBlock:^NSFetchRequest *(NSURL *URL) {
        RKPathMatcher *pathMatcher = [RKPathMatcher pathMatcherWithPattern:itemsByLocationRequestPath];
        
        NSDictionary *argsDict = nil;
        BOOL match = [pathMatcher matchesPath:[URL relativePath] tokenizeQueryStrings:NO parsedArguments:&argsDict];
        
        if (match) {
            NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"ItemsByLocation"];
            return fetchRequest;
        }
        
        return nil;
    }];
    
        RKEntityMapping *itemsByLocationListMapping = [RKEntityMapping mappingForEntityForName:@"ItemsByLocation" inManagedObjectStore:managedObjectStore];
        itemsByLocationListMapping.identificationAttributes = @[@"itemId"];
    
        [itemsByLocationListMapping addAttributeMappingsFromDictionary:
         @{
           @"itemId" : @"itemId",
           @"itemName" : @"itemName",
           @"itemCode" : @"itemCode",
           @"uomCode" : @"uomCode",
           @"locId" : @"locId",
           @"locName" : @"locName",
           @"subLocId" : @"subLocId",
           @"subLocName" : @"subLocName",
           @"storeItemId" : @"storeItemId",
           @"stockTakeQtyId" : @"stockTakeQtyId",
           @"countedTime" : @"countedTime",
           @"countQty" : @"countQty",
           @"removed" : @"removed",
           @"remarks" : @"remarks"
           }
         ];
    
        RKResponseDescriptor *itemsByLocationListResponseDescriptor =
        [RKResponseDescriptor responseDescriptorWithMapping:itemsByLocationListMapping
                                                     method:RKRequestMethodGET
                                                pathPattern:@"/stocktake/stocktake/:id/loc/:id/usr/:id/items"
                                                    keyPath:nil
                                                statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)
         ];
    
        [objectManager addResponseDescriptor:itemsByLocationListResponseDescriptor];

在 View Controller-3 上加载数据:

    [[RKObjectManager sharedManager]
     getObjectsAtPath:itemsByLocationRequestPath
     parameters:nil
     success: ^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) {
         NSManagedObjectContext *context = [RKManagedObjectStore defaultStore].mainQueueManagedObjectContext;
    NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"ItemsByLocation"];
    
    NSSortDescriptor *descriptor = [NSSortDescriptor sortDescriptorWithKey:@"subLocName" ascending:YES];
    fetchRequest.sortDescriptors = @[descriptor];
    
    NSError *error = nil;
    [context executeFetchRequest:fetchRequest error:&error];
          NSError *error;
    if (![[self fetchedResultsController] performFetch:&error]) {

    }
         [self.tableView reloadData];
         NSLog(@"requestDataItemsForStore - Mapping Success");
     }
     failure: ^(RKObjectRequestOperation *operation, NSError *error) {
         RKLogError(@"Load failed with error: %@", error);
         NSLog(@"requestDataItemsForStore - Loading Failed");
     }
     ];

你能帮忙解决这个问题吗?

【问题讨论】:

  • @Wain,这里需要你的帮助。提前致谢。

标签: ios core-data restkit


【解决方案1】:

这是一个线程和/或多上下文问题。您的实体缓存是在一个上下文中设置的,但是您在不同的上下文中使用它(这就是实体名称相同但不同的原因)。

这可能是因为您正在设置多个不同的核心数据堆栈(从代码示例中看起来很像)。在这种情况下,您应该将所有核心数据堆栈和映射分解出来,以便将代码创建到一个新类 - 数据管理器中。将数据管理器传递给每个视图控制器,以便他们可以使用它来下载所需的内容。

如果是线程问题,那么打开核心数据线程异常可以帮助您找到原因。

请注意,在每个视图控制器中使用 FRC 很好,但您应该只使用一个主线程上下文来设置它们。

【讨论】:

  • 感谢您的回复。你能用一个例子解释一下这需要如何实现吗?
  • 基本上只是从一个视图控制器中获取当前代码并将其移动到另一个类,删除原始代码。然后从其他视图控制器中移动您的映射,并从这些视图控制器中删除所有设置和映射代码。现在您将所有配置代码放在一个地方,因此它只运行一次(它应该在类实例化时运行,并且您应该从应用程序委托实例化它)
  • 感谢@Wain 的帮助。最后我放弃了 RestKit,因为它的文档不好。我在 RestKit 中遇到了一个又一个问题。
  • 如果问题是因为应用程序使用其他 managedObjectContext ,因为这是我的情况,有两种可能性。第一种是使用由 RestKit 创建的 managedObjectContext,“managedObjectStore.mainQueueManagedObjectContext”。另一种可能性是禁用缓存,这样更快
猜你喜欢
  • 1970-01-01
  • 2013-06-06
  • 1970-01-01
  • 1970-01-01
  • 2019-12-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多