RestKit postObject 方法中的 post 对象与它返回的 RKMappingResult 有啥关系?

Posted

技术标签:

【中文标题】RestKit postObject 方法中的 post 对象与它返回的 RKMappingResult 有啥关系?【英文标题】:What is the relationship between the post object, in the RestKit postObject method, and the RKMappingResult it returns?RestKit postObject 方法中的 post 对象与它返回的 RKMappingResult 有什么关系? 【发布时间】:2014-03-12 05:54:24 【问题描述】:

我一直在查看有关 RestKit 的文档,但我无法弄清楚 RKMappingResult 上的一些细节。

我的代码创建了一个名为newUserNSManagedObject,并插入了RestKit RKManagedObjectStore 的mainQueueManagedObjectContext。然后我使用对象管理器的postObject:path:parameters:success:failure: 方法将用户发布到服务器。这似乎工作正常。

当发布请求完成时,我需要更新newUser 托管对象上的一些字段,这些字段不会从发布请求的结果映射。传递到块中的mappingResult 参数似乎具有与服务器响应中设置的所有字段正确映射的托管对象。

显然,mappingResult 中的对象与我发布的对象不同,因为它们不在同一个线程中。如果我在发布之前保存newUser,他们的objectID 在请求完成并返回映射结果时是否相同?

我想象newUser 和映射结果中的对象都引用存储在 CoreData 中的同一个对象,对吗?我问的原因是,情况似乎并非如此。如果我发出一个发布请求并同时保存newUser 对象和mappingResult 中返回的对象,我最终会在Core Data 中得到两个不同的对象,一个用于newUser,一个用于映射结果。我通过更改 mappingResult 对象和 newUser 对象上的字段并看到它们都独立更改来确认这一点。此外,下次我获取时获取两个用户对象。我该如何防止这种情况?

如果这是正确的,如果我崩溃了,请求尚未完成会发生什么?我不会在 CoreData 中有一个存根对象吗?

如果请求失败,如何删除我创建的存根对象?我可以在故障块中执行以下操作吗?

        dispatch_async(dispatch_get_main_queue(), ^
            [newUser.managedObjectContext deleteObject:newRegisteredUser];
        );

newUser 是我在 mainQueue 上创建的托管对象。

最后,如果我通过映射结果在后台线程中取回托管对象,我无法将 returnedUser 设置为 UI 用来从中获取信息的用户,因为它位于主线程上,那么如何通知 UI 以更改其用户?我应该发布一个通知,告诉 UI 从 Core Data 重新获取活动用户吗?

关于重复对象的更新

我已经做了一些进一步的测试,当帖子返回时,我肯定会得到一个重复的对象。

我首先创建一个newUser 并在其上设置属性:

HNGRRegisteredUser *newUser = [NSEntityDescription insertNewObjectForEntityForName:@"RegisteredUser" inManagedObjectContext:context];
newUser.firstName = @"John"
newUser.lastName = @"Smith"
newUser.gender = @"Male"
newUser.email = @"js@email.com"
newUser.hungrosityModel = //fill in all of the attributes for a hungrosityModel, except uniqueID

此时,newUser.uniqueID == nil。然后我保存 newUser(我假设我必须这样做,RestKit 可以使用永久 objectID),以创建一个存根对象。

NSError *saveError;
[newRegisteredUser.managedObjectContext saveToPersistentStore:&saveError];
if (saveError) NSLog(@"Save Error: %@", saveError);
NSLog(@"newUser objectID: %@", [newRegisteredUser.objectID URIRepresentation]);

然后我发布newUser 对象。

[[RKObjectManager sharedManager] postObject:newRegisteredUser path:@"users/" parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *mappingResult) 
    NSError *saveError;
    HNGRRegisteredUser *returnedUser = [mappingResult firstObject];
    NSLog(@"mappingResult objectID: %@", [returnedUser.objectID URIRepresentation]);

    [returnedUser.managedObjectContext saveToPersistentStore:&saveError];
    if (saveError) NSLog(@"Error saving user upon registration: %@", saveError);
 failure:^(RKObjectRequestOperation *operation, NSError *error) 
    [newUser.managedObjectContext deleteObject:newUser];
];

objectID 的打印输出如下。

newUser objectID: x-coredata://B74AB613-1060-4D98-ABFC-3B4D89AB12C3/RegisteredUser/p2
mappingResult objectID: x-coredata://B74AB613-1060-4D98-ABFC-3B4D89AB12C3/RegisteredUser/p3

发送到服务器的 JSON 是:


    "gender":"Male",
    "lastName":"Smith",
    "firstName":"John",
    "email":"js@email.com",
    "hungrosityModel": 
        "totalNumberOfUpdates":0,
        "peakHungrosity":0.8,
        "hungrosityFractionAtLastUpdate":0,
        "rateOfHungrosification":5e-05,
        "troughHungrosity":0.1,
        "peakUpdates":0
    

从服务器返回的 JSON 是:

"results": [
    "receivedFriendRequests": [],
     "firstName": "John",
     "middleName": null,
     "hungrosityModel": "uniqueID": 13,
     "email": "js@email.com",
     "gender": "Male",
     "lastName": "Smith",
     "sentFriendRequests": [],
     "uniqueID": 14,
     "updatedAt": "2014-03-12T17:45:01.973Z",
     "friends": [],
     "profileImageUpdatedAt": null,
     "createdAt": "2014-03-12T17:45:01.809Z"
 ]

对象的响应和请求描述符是:

[[RKObjectManager sharedManager].router.routeSet addRoute:[RKRoute routeWithClass:[HNGRRegisteredUser class] pathPattern:@"users/" method:RKRequestMethodPOST]];

RKResponseDescriptor *registrationResponseDescriptor = [RKResponseDescriptor responseDescriptorWithMapping:[HNGRRKMappingProvider registeredUserMapping] method:RKRequestMethodPOST pathPattern:@"users/" keyPath:@"results" statusCodes:RKStatusCodeIndexSetForClass(RKStatusCodeClassSuccessful)];
[[RKObjectManager sharedManager] addResponseDescriptor: registrationResponseDescriptor];

RKRequestDescriptor *registrationRequestDescriptor = [RKRequestDescriptor requestDescriptorWithMapping:[[HNGRRKMappingProvider registeredUserMapping] inverseMapping] objectClass:[HNGRRegisteredUser class] rootKeyPath:nil method:RKRequestMethodPOST];
[[RKObjectManager sharedManager] addRequestDescriptor:registrationRequestDescriptor];

用户的映射是:

RKEntityMapping *_mapping = nil;
_mapping = [RKEntityMapping mappingForEntityForName:@"RegisteredUser" inManagedObjectStore:[RKManagedObjectStore defaultStore]];
[_mapping addAttributeMappingsFromArray:@[@"uniqueID",
                                           @"createdAt",
                                           @"updatedAt",
                                           @"firstName",
                                           @"middleName",
                                           @"lastName",
                                           @"email",
                                           @"gender",
                                           @"profileImageUpdatedAt"]];
[_mapping addRelationshipMappingWithSourceKeyPath:@"hungrosityModel" mapping:[HNGRRKMappingProvider basicModelMapping]];
_mapping.identificationAttributes = @[@"uniqueID"];

这是相关的对象映射跟踪:

D restkit.object_mapping:RKMapperOperation.m:377 Executing mapping operation for representation: 
        results =     (
                    
                createdAt = "2014-03-12T18:22:35.688Z";
                dateOfBirth = "2014-03-12T18:24:02Z";
                firstName = John;
                friends =             (
                );
                gender = Male;
                hungrosityModel =             
                    uniqueID = 14;
                ;
                lastName = Smith;
                middleName = "<null>";
                profileImageUpdatedAt = "<null>";
                receivedFriendRequests =             (
                );
                sentFriendRequests =             (
                );
                uniqueID = 14;
                updatedAt = "2014-03-12T18:22:35.856Z";
            
        );
    
     and targetObject: <HNGRRegisteredUser: 0xaa8c180> (entity: RegisteredUser; id: 0xa894550 <x-coredata://44B8DF7A-BDC9-45F2-A137-BFAACF4AAF88/RegisteredUser/p2> ; data: 
        createdAt = nil;
        dateOfBirth = "2014-03-12 18:24:02 +0000";
        email = "js@email.com";
        firstName = John;
        friendRequests = "<relationship fault: 0xa899dd0 'friendRequests'>";
        friends = "<relationship fault: 0xa891ea0 'friends'>";
        gender = Male;
        hungrosityModel = "0xa861980 <x-coredata://44B8DF7A-BDC9-45F2-A137-BFAACF4AAF88/BasicModel/p2>";
        hungrosityUpdateEvents = "<relationship fault: 0xa829d90 'hungrosityUpdateEvents'>";
        isLocalUser = 0;
        lastName = Smith;
        middleName = nil;
        myInvitations = nil;
        profileImageData = nil;
        profileImageThumbnailData = nil;
        profileImageUpdatedAt = nil;
        receivedInvitations = nil;
        registeredUserSettings = nil;
        savedHungrosityComments = "<relationship fault: 0xa8bc450 'savedHungrosityComments'>";
        uniqueID = nil;
        updatedAt = nil;
        user = nil;
        userSettings = nil;
    )
  T restkit.object_mapping:RKMapperOperation.m:320 Examining keyPath 'results' for mappable content...
  D restkit.object_mapping:RKMapperOperation.m:297 Found mappable collection at keyPath 'results': (
            
            createdAt = "2014-03-12T18:22:35.688Z";
            dateOfBirth = "2014-03-12T18:24:02Z";
            firstName = John;
            friends =         (
            );
            gender = Male;
            hungrosityModel =         
                uniqueID = 14;
            ;
            lastName = Smith;
            middleName = "<null>";
            profileImageUpdatedAt = "<null>";
            receivedFriendRequests =         (
            );
            sentFriendRequests =         (
            );
            uniqueID = 14;
            updatedAt = "2014-03-12T18:22:35.856Z";
        
    )

 D restkit.object_mapping:RKMappingOperation.m:952 Starting mapping operation...
 T restkit.object_mapping:RKMappingOperation.m:953 Performing mapping operation: <RKMappingOperation 0xa8e4440> for 'HNGRRegisteredUser' object. Mapping values from object 
    createdAt = "2014-03-12T18:22:35.688Z";
    dateOfBirth = "2014-03-12T18:24:02Z";
    firstName = John;
    friends =     (
    );
    gender = Male;
    hungrosityModel =     
        uniqueID = 14;
    ;
    lastName = Smith;
    middleName = "<null>";
    profileImageUpdatedAt = "<null>";
    receivedFriendRequests =     (
    );
    sentFriendRequests =     (
    );
    uniqueID = 14;
    updatedAt = "2014-03-12T18:22:35.856Z";
 to object <HNGRRegisteredUser: 0xa8833b0> (entity: RegisteredUser; id: 0xa8777e0 <x-coredata:///RegisteredUser/t1FD3D8DE-75AD-4194-B157-EB6931B74BDB6> ; data: 
    createdAt = nil;
    dateOfBirth = nil;
    email = nil;
    firstName = nil;
    friendRequests =     (
    );
    friends =     (
    );
    gender = nil;
    hungrosityModel = nil;
    hungrosityUpdateEvents =     (
    );
    isLocalUser = 0;
    lastName = nil;
    middleName = nil;
    myInvitations = nil;
    profileImageData = nil;
    profileImageThumbnailData = nil;
    profileImageUpdatedAt = nil;
    receivedInvitations = nil;
    registeredUserSettings = nil;
    savedHungrosityComments =     (
    );
    uniqueID = 14;
    updatedAt = nil;
    user = nil;
    userSettings = nil;
) with object mapping (null)

如果有更多信息对了解有帮助,请告诉我。

【问题讨论】:

【参考方案1】:

是的,对象 id 将始终匹配(即使托管对象实例不同,它仍然代表相同的底层实体实例)。通常它们将是同一个托管对象实例,因为您在主线程上启动,RestKit 也会在主线程上调用您。

您描述的新实例没有发生。您发布的原始对象应直接更新。在您的情况下,这没有发生,因为服务器正在返回一个对象数组。这导致 RestKit 忽略提供的目标对象并创建新的目标对象。在这种情况下,这是一个新对象,因为数组包含一项。要解决此问题,您需要修改来自服务器的 JSON,因为您无法将数组作为映射的一部分进行索引...

要删除,您不需要切换线程(但如果您想将来打样也可以),这是正确的方法。

您可以发布通知。我通常使用 fetched results 控制器,因为它会为您管理观察变化。

【讨论】:

非常有帮助,您就像这里的 RESTKit 的守护者一样。它们目前确实有不同的唯一标识符,因为服务器需要在对象上设置 uniqueID。 newUser 上的 uniqueID 为 nil。我将提取所有相关代码并在一个独立的应用程序中对其进行测试,因为我知道它确实像我预期的那样工作。我会尽快回复你确切的细节和精简的代码。获取的结果控制器不是通常用于表视图吗?在这种情况下我没有使用。 FRC 通常与表格视图一起使用,但也可以在其他情况下使用(您不需要使用部分,实际上只是一个结果列表)。 我明白了。我当然应该对此进行更多研究。我更新了我的问题以包含映射、JSON 和跟踪日志。 你使用的是什么版本的 RestKit? 我认为问题在于服务器正在向您返回一个对象数组。你能改变它吗?我只是在检查我的假设...

以上是关于RestKit postObject 方法中的 post 对象与它返回的 RKMappingResult 有啥关系?的主要内容,如果未能解决你的问题,请参考以下文章

RestKit .20 RKRequestDescriptor postObject - (400-499) 中的预期状态代码,得到 200

RestKit - postObject 跳过参数

在 iOS 的 RestKit 中执行 postObject 时忽略响应

Restkit 0.2 postObject 总是返回 request.body=(null)

带有查询参数的 RestKit postObject

Restkit 0.22.0 和 iOS 7 RKObjectManager postObject 不生成 JSON 数据