gpt4 book ai didi

ios - Restkit:执行我自己的 RKMapperOperation 时创建重复对象

转载 作者:可可西里 更新时间:2023-11-01 06:07:25 25 4
gpt4 key购买 nike

我有一个 iOS 应用程序,它使用 restkit 来处理 json 响应以将事物映射到核心数据中。每当我通过 RKObjectManager 的 get/post/put/delete 方法执行请求时,它工作得很好,而且我从来没有遇到任何问题。

我正在开发的应用程序还支持套接字更新,为此我使用 SocketIO 来处理。 SocketIO 也能正常工作,服务器发出的每个事件我都会收到,除非应用程序当时没有运行。

当我尝试从套接字事件中获取 json 数据并将其映射到核心数据时,就会出现问题。如果套接字事件同时出现,响应从我通过 RKObjectManager 发出的请求返回,并且它们都是第一次给我相同的对象,它们通常最终都会在 coredata 中制作同一个 ManagedObject 的 2 个副本,我在控制台中收到以下警告:

托管对象缓存为 [modelObjectName] 实体配置的标识符返回了 2 个对象,预期为 1。

这是我制作的方法,其中包含制作 RKMapperOperation 的代码:

+(void)createOrUpdateObjectWithJSONDictionary:(NSDictionary*)jsonDictionary
{
RKManagedObjectStore* managedObjectStore = [CMRAManager sharedInstance].objectManager.managedObjectStore;

NSManagedObjectContext* context = managedObjectStore.mainQueueManagedObjectContext;

[context performBlockAndWait:^{
RKEntityMapping* modelEntityMapping = [self entityMappingInManagedObjectStore:managedObjectStore];

NSDictionary* modelPropertyMappingsByDestinationKeyPath = modelEntityMapping.propertyMappingsByDestinationKeyPath;
NSString* modelMappingObjectIdSourceKey = kRUClassOrNil([modelPropertyMappingsByDestinationKeyPath objectForKey:NSStringFromSelector(@selector(object_Id))], RKPropertyMapping).sourceKeyPath;
NSString* modelObjectId = [jsonDictionary objectForKey:modelMappingObjectIdSourceKey];

CMRARemoteObject* existingObject = [self searchForObjectOfCurrentClassWithId:modelObjectId];


RKMapperOperation* mapperOperation = [[RKMapperOperation alloc]initWithRepresentation:jsonDictionary mappingsDictionary:@{ [NSNull null]: modelEntityMapping }];
[mapperOperation setTargetObject:existingObject];

RKManagedObjectMappingOperationDataSource* mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc]initWithManagedObjectContext:context cache:managedObjectStore.managedObjectCache];
[mappingOperationDataSource setOperationQueue:[NSOperationQueue new]];
[mappingOperationDataSource setParentOperation:mapperOperation];

[mappingOperationDataSource.operationQueue setMaxConcurrentOperationCount:1];
[mappingOperationDataSource.operationQueue setName:[NSString stringWithFormat:@"%@ with operation '%@'", NSStringFromSelector(_cmd), mapperOperation]];

[mapperOperation setMappingOperationDataSource:mappingOperationDataSource];

NSError* mapperOperationError = nil;
BOOL mapperOperationSuccess = [mapperOperation execute:&mapperOperationError];
NSAssert((mapperOperationError == nil) && (mapperOperationSuccess == TRUE), @"Execute mapperOperation error");
if (mapperOperationError || (mapperOperationSuccess == FALSE))
{
NSLog(@"mapperOperationError: %@",mapperOperationError);
}

NSError* contextSaveError = nil;
BOOL contextSaveSuccess = [context saveToPersistentStore:&contextSaveError];
NSAssert((contextSaveError == nil) && (contextSaveSuccess == TRUE), @"Save context error");


}];
}

在上面的代码中,我首先尝试获取现有的托管对象(如果它当前存在)以将其设置为映射器请求的目标对象。查找对象的方法 (searchForObjectOfCurrentClassWithId:) 如下所示:

+(instancetype)searchForObjectOfCurrentClassWithId:(NSString*)objectId
{
NSManagedObjectContext* context = [CMRAManager sharedInstance].objectManager.managedObjectStore.mainQueueManagedObjectContext;

__block CMRARemoteObject* fetchedObject = nil;

[context performBlockAndWait:^{

NSFetchRequest* fetchRequest = [self fetchRequestForCurrentClassObjectWithId:objectId];
NSError* fetchError = nil;
NSArray *entries = [context executeFetchRequest:fetchRequest error:&fetchError];

if (fetchError)
{
NSLog(@"fetchError: %@",fetchError);
return;
}

if (entries.count != 1)
{
return;
}

fetchedObject = kRUClassOrNil([entries objectAtIndex:0], CMRARemoteObject);
if (fetchedObject == nil)
{
NSAssert(FALSE, @"Should be of this class");
return;
}

}];

return fetchedObject;
}

我对这里问题的最佳猜测是,这可能是由于托管对象上下文及其线程造成的。我对它们应该如何工作没有最好的理解,因为我已经能够依赖 Restkit 对它的正确使用。我已尽力复制 Restkit 设置这些映射操作的方式,但我假设我在上述代码中的某个地方犯了错误。

如果有帮助,我愿意发布任何其他代码。我没有发布我的 RKEntityMapping 代码,因为我很确定错误不在那里 - 毕竟,Restkit 在执行映射器操作时已经成功地映射了这些对象,即使有冗余的 JSON 对象/数据到 map 。

我认为问题一定是我对托管对象上下文及其线程的错误实现的另一个原因是因为我正在 iPhone 5c 和 iPod touch 上进行测试,而问题不会发生在iPod touch,我认为它只有 1 个核心,但 iPhone 5c 确实有时会遇到这个问题,我相信它有多个核心。我要强调的是,我不确定我在本段中所做的陈述是否一定是正确的,所以不要以为我知道我在谈论设备内核,这只是我认为我已经读过的东西之前。

最佳答案

尝试改变:

RKManagedObjectMappingOperationDataSource* mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc]initWithManagedObjectContext:context cache:managedObjectStore.managedObjectCache];

到:

RKManagedObjectMappingOperationDataSource* mappingOperationDataSource = [[RKManagedObjectMappingOperationDataSource alloc]initWithManagedObjectContext:context cache:[RKFetchRequestManagedObjectCache new]];

这是在保存持久上下文之前的良好措施:

// Obtain permanent objectID
[[RKObjectManager sharedManager].managedObjectStore.mainQueueManagedObjectContext obtainPermanentIDsForObjects:[NSArray arrayWithObject:mapperOperation.targetObject] error:nil];

编辑#1

尝试删除这些行:

    [mappingOperationDataSource setOperationQueue:[NSOperationQueue new]];
[mappingOperationDataSource setParentOperation:mapperOperation];

[mappingOperationDataSource.operationQueue setMaxConcurrentOperationCount:1];
[mappingOperationDataSource.operationQueue setName:[NSString stringWithFormat:@"%@ with operation '%@'", NSStringFromSelector(_cmd), mapperOperation]];

编辑#2

从 RKManagedObjectMappingOperationDataSourceTest.m 查看这个单元测试。您是否设置了 identificationAttributes 以防止重复?可能没有必要查找和设置 targetObject,我认为 RestKit 会尝试在未设置的情况下查找现有对象。还可以尝试在使用 [store newChildManagedObjectContextWithConcurrencyType:NSPrivateQueueConcurrencyType tracksChanges:NO] 创建的私有(private)上下文上执行对象映射,在保存上下文后,应将更改推送到主上下文。

- (void)testThatMappingObjectsWithTheSameIdentificationAttributesAcrossTwoContextsDoesNotCreateDuplicateObjects
{
RKManagedObjectStore *managedObjectStore = [RKTestFactory managedObjectStore];
RKInMemoryManagedObjectCache *inMemoryCache = [[RKInMemoryManagedObjectCache alloc] initWithManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
managedObjectStore.managedObjectCache = inMemoryCache;
NSEntityDescription *humanEntity = [NSEntityDescription entityForName:@"Human" inManagedObjectContext:managedObjectStore.persistentStoreManagedObjectContext];
RKEntityMapping *mapping = [RKEntityMapping mappingForEntityForName:@"Human" inManagedObjectStore:managedObjectStore];
mapping.identificationAttributes = @[ @"railsID" ];
[mapping addAttributeMappingsFromArray:@[ @"name", @"railsID" ]];

// Create two contexts with common parent
NSManagedObjectContext *firstContext = [managedObjectStore newChildManagedObjectContextWithConcurrencyType:NSPrivateQueueConcurrencyType tracksChanges:NO];
NSManagedObjectContext *secondContext = [managedObjectStore newChildManagedObjectContextWithConcurrencyType:NSPrivateQueueConcurrencyType tracksChanges:NO];

// Map into the first context
NSDictionary *objectRepresentation = @{ @"name": @"Blake", @"railsID": @(31337) };

// Check that the cache contains a value for our identification attributes
__block BOOL success;
__block NSError *error;
[firstContext performBlockAndWait:^{
RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:firstContext
cache:inMemoryCache];
RKMapperOperation *mapperOperation = [[RKMapperOperation alloc] initWithRepresentation:objectRepresentation mappingsDictionary:@{ [NSNull null]: mapping }];
mapperOperation.mappingOperationDataSource = dataSource;
success = [mapperOperation execute:&error];
expect(success).to.equal(YES);
expect([mapperOperation.mappingResult count]).to.equal(1);

[firstContext save:nil];
}];

// Check that there is an entry in the cache
NSSet *objects = [inMemoryCache managedObjectsWithEntity:humanEntity attributeValues:@{ @"railsID": @(31337) } inManagedObjectContext:firstContext];
expect(objects).to.haveCountOf(1);

// Map into the second context
[secondContext performBlockAndWait:^{
RKManagedObjectMappingOperationDataSource *dataSource = [[RKManagedObjectMappingOperationDataSource alloc] initWithManagedObjectContext:secondContext
cache:inMemoryCache];
RKMapperOperation *mapperOperation = [[RKMapperOperation alloc] initWithRepresentation:objectRepresentation mappingsDictionary:@{ [NSNull null]: mapping }];
mapperOperation.mappingOperationDataSource = dataSource;
success = [mapperOperation execute:&error];
expect(success).to.equal(YES);
expect([mapperOperation.mappingResult count]).to.equal(1);

[secondContext save:nil];
}];

// Now check the count
objects = [inMemoryCache managedObjectsWithEntity:humanEntity attributeValues:@{ @"railsID": @(31337) } inManagedObjectContext:secondContext];
expect(objects).to.haveCountOf(1);

// Now pull the count back from the parent context
NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"Human"];
fetchRequest.predicate = [NSPredicate predicateWithFormat:@"railsID == 31337"];
NSArray *fetchedObjects = [managedObjectStore.persistentStoreManagedObjectContext executeFetchRequest:fetchRequest error:nil];
expect(fetchedObjects).to.haveCountOf(1);

关于ios - Restkit:执行我自己的 RKMapperOperation 时创建重复对象,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/24683836/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com