RestKit: Managed object mapped inside a plain old

2019-05-30 04:37发布

问题:

Preamble: I'm using RestKit 0.21.0, but I've also tried 0.23.0.

I have a number of mappings where a plain old Objective-C object (let's call that a POOCO) contains an NSManagedObject. It seems that when doing GET requests on this root POOCO, the managed object is created under the mainQueueManagedObjectContext.

However when I do a PUT against the POOCO, when the response is loaded (the response is a copy of the object that was sent), the managed sub-object is created under a temporary NSManagedObjectContext, whose parent is the mainQueueManagedObjectContext.

A quick aside on re-fetching in RestKit:

If the request were composed entirely of NSManagedObject instances, the resulting objects would be "refetched" using (I think) the mainQueueManagedObjectContext. (IIRC, once all the object mapping is complete, the objects and sub-objects would all be re-fetched from the main MOC, replacing the objects that were created on the temporary MOC used for mapping.)

This refetching is done (as far as I can tell) because when a MOC gets dealloc'd, all of the managed objects that it manages become invalid. So we refetch the objects in a MOC to which myObjectManager.managedObjectStore maintains a strong reference, so that they remain valid after the temporary MOC goes away.

Because the root object in the mapping is a POOCO, any references it has to managed objects do not get refetched, so they remain attached to the temporary MOC, and become invalid once mapping is complete.

Has anyone else run into issues like this? Are there best practices you can suggest? Is it possible to tell RestKit, "refetch these managed objects using the mainQueueManagedObjectContext" or anything like that?

回答1:

Managed object instances can't be passed between threads, and RestKit does the mapping on background threads. If the returned objects are managed objects then they will be transferred to the main thread for you. It could be argued that nested managed objects not being switched to the main thread for you is a bug / oversight in RestKit (that you could raise as an issue in github).

You can ask RestKit to use the main thread for some things (by explicitly creating the operations and running them, though this doesn't cover everything) but you really don't want to do that.

So, your main option is to have a method in your container object which you can call in the success block and which iterates through the contained managed objects and 'refetches' them - as you know that all the objects exist this can be done with objectWithID:, and the success block is called on the main thread.



回答2:

The question actually has some misconceptions about when RestKit performs refetching. Under the circumstances described in the question, RestKit actually does return an instance of RKRefetchingMappingResult in the success block. Calling any of the accessors on that object (such as array or firstObject) actually does perform the refetching. The mistake was that I was never invoking any of these methods on the RKMappingResult.

My assumption was that, since the object I was sending in the PUT request would become the operation's targetObject, the object would be updated in-place by RestKit. This is why I never felt the need to do anything with the RKMappingResult. The assumption isn't technically wrong, but by accessing the object directly once the mapping was complete instead of using the RKMappingResult, I was skipping the refetching step.

I've proposed a change in this behaviour in an issue I've filed with the RestKit folks, to make refetching automatic in certain circumstances.

To summarize:

When making a request using RKManagedObjectRequestOperation (whether you specify that manually or RestKit selects this class for you), always make sure you exercise the RKMappingResult to ensure that the results are re-fetched from the correct NSManagedObjectContext.