How to handle external data cleaning when deleting * unsaved Core Data objects?

In the managed object, I saved the path to the image file in the application container.
When the deleted object is deleted, the image file must be moved to the trash. This should be done as early as possible, so that I can provide functionality to undo as long as possible.

I followed the answers to this question: How to handle cleaning external data when deleting Core Data objects and overriding -didSave in my subclass under the managed object, delete the files.

Turns off, this only works if:

  • managed object added
  • Managed object context saved
  • deleted object deleted
  • The context of the managed entity is saved.

In the following case, however, -Save is not called in the managed entity:

  • managed object added
  • deleted object deleted
  • The context of the managed entity is saved.

I understand why this is happening. Since the deleted object was never saved in the first place, it will not be saved after deletion, -didSave is not called.

Now I'm looking for another place from which you can move the link file to the trash. Where could it be?

+7
cocoa core-data nsmanagedobject nsmanagedobjectcontext
source share
2 answers

You have two options for implementing this method using managed entity methods: using managed entity life cycle events or using validation. However, there are some tradeoffs and risks. There are other approaches that may work best for you (see Recommendations).

Managed Object Life Cycle

Managed objects are monitored by the NSManagedObjectContext that they own. It is “managed” in a managed entity. Most instances of NSManagedObject actually executed by NSManagedObjectContext . The managed object is informed of changes in the life cycle event methods : awakeFromFetch , awakeFromInsert , awakeFromSnapshotEvents: didSave , didTurnIntoFault , prepareForDeletion , etc. When implementing these methods, you must be careful not to modify the managed object in such a way that it would prevent it from being “dirty” in context or otherwise change the current transaction. For example, an attempt to “resurrect” a deleted object in didSave or change the relationship in awakeFromFetch or access the property in didTurnIntoFault (which should lead to an error, which would be bad).

During a typical deletion of a stored object, life cycle events are called in the following order:

If subsequently saving is performed in the context of the parent object, additional life cycle events may occur in instances belonging to these contexts. This can be very important to keep in mind when working with shared resources outside of master data.

When an object has not been saved and removed from the context, life cycle events occur in the following order:

Using lifecycle methods of a managed entity may not be a good solution in this case. As you can see, prepareForDeletion is called in scripts that interest you, but this happens before the deletion is checked in the case of a save operation.

Check

Validation is an important feature of Core Data. When objects are saved, Core Data applies the validation rules set in the model, as well as any custom validation implemented in the NSManagedObject classes. In the case of deletion, the master data applies the deletion rules defined in the model as part of the save operation. prepareForDeletion is called before the check is done - therefore, if you processed the data as part of prepareForDeletion , you can delete the data for an object that will not actually be deleted as part of the save. This may cause some problems.

You can implement your deletion as part of validateForDelete: or as a custom validation method that checks the state of an object ( isDeleted , isInserted , etc.). Superversion validateForDelete: will follow the delete rules, be sure to call it accordingly. The check will be called automatically as part of the save operation, but you can call it manually at any time (and this is recommended). To perform a manual check, call the appropriate method from your application, in this case validateForDelete: Check the result of BOOL, and if it returns NO, handle the error accordingly.

Recommendations

It is best to implement writing image data to the local file system as part of a check or save. When Core Data performs a save, it essentially makes all changes in the context as a transaction. When working with external resources, it can make a lot of sense to make a change to these external resources within the same process. For example, in your validateImageURL:error: method, you should at least claim that the given URL is the address of the local file system and that you can write to it. In willSave / didSave you can write the URL specified by imageURL if the object was inserted or updated and delete the data in imageURL if it was deleted. In the case of an object that has not yet been saved but is removed from the context, the data will not be transferred to the local file array yet. It will exist only in memory, like everything connected with the object.

Note that no matter how you read, write, or delete external data, you must do this using the NSFileCoordinator API to coordinate access to files and directories.

There are still problems with this approach. An NSManagedObjectContext object (and its objects) is just a collection of data references in persistent storage. If you save external data from NSManagedObject , you may run into problems when you have multiple contexts, nested contexts (which you MUST use!), Etc. NSPsistentStore is what controls the storage of NSManagedObject data and, ideally, your interaction with the fighttime will happen at this level, which will solve some of the problems that I mentioned, and much more. The best way to do this is to use external Core Data storages to manage this data, as they are already built into (some) persistent storages. You can also try to subclass NSPersistentStoreCoordinator and override the executeRequest:withContext:error: method to implement your own external storage.

+9
source share

Great point - I also updated my answer on this question and I will expand my favorite approach here:

  • Forget about will / did save. In this case is not reliable.

  • Implement prepareForDeletion: If you do not need to cancel and make sure that the removal was successful, delete the file in place. Otherwise, add the file to a convenient registry (a NSMutableSet owned by the context owner, or some of them.)

  • If you need to undo / redo, run awakeFromSnapshotEvents: to catch the delete and re-delete. Delete / re-add the file from / to the registry as needed.

  • Register a notification about didSave anywhere conveniently. When rescue occurs, delete all files listed in the registry and clean them.

All this assumes, by the way, that not one of your own objects will ever own the same file. If possible, things get a lot more complicated, but I suppose you specifically tuned your model to prevent this from happening.

+1
source share

All Articles