mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-11 20:00:30 +01:00
Get transaction that wraps a managed context #29
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Originally created by @mantas on GitHub (Jan 6, 2016).
I frequently bump into situation when I'd like to do smth in the context of a specific object. In pure CoreData, I'd run those operation on object.managedObjectContext and all is good. Now've to keep track of both the object and transaction it's in. This adds quite a a bit of unnecessary complexity to the code.
I see there's NSManagedObjectContext#parentTransaction method that does exactly what I was looking for. But it's marked as internal. Is there a reason why I shouldn't touch it? Is there some common pattern how to work around this issue?
@JohnEstropia commented on GitHub (Jan 6, 2016):
@mantas Part of the safety offered by CoreStore is that transactions guarantee that your NSManagedObjectContext operations always run on queues managed by CoreStore.
But you can use
UnsafeDataTransactionfor exceptional needs:As the name
Unsafesuggests though, this basically lets you trade safety for flexibility. And you should still usetransaction.commit()to save your changes (instead ofcontext.save())@JohnEstropia commented on GitHub (Jan 6, 2016):
@mantas I'm curious though, what tasks do you need to do where you need the context instance directly? If you can tell me your use cases I might be able to give you an alternative way to do with CoreStore (or even add a new feature for it)
@mantas commented on GitHub (Jan 8, 2016):
@JohnEstropia I'm looking for a reverse operation. In fact, I see that it exists, but it's marked as internal. I wonder why it was marked for internal use only
I sometimes pass around an object and later needs to fetch different object in the same context. Lets say, I've a blog post and I may want to edit tags later down. I'd pass around the BlogPost object and get it's transaction whenever I need to fetch the tags. That works very well with CoreData - I can always do BlogPost#managedObjectContext. But since I can't get it's CoreStore transaction, I've to pass around both BlogPost and it's transaction.
@JohnEstropia commented on GitHub (Jan 8, 2016):
@mantas Okay, I think you're asking the same thing as here: https://github.com/JohnEstropia/CoreStore/issues/34
Basically instead of passing around an object, you have to think of managing who owns that object instead. It's just either one of these:
DataStack(where you fetch objects withCoreStore.fetch...(),yourDataStack.fetch...()or withListMonitorandObjectMonitor)CoreStore.beginAsynchronous(...)), but you can't bring this and its objects outside the transaction's closureCoreStore.beginSynchronous(...)), same with above, you can't pass this outside its scopeCoreStore.beginUnsafe()). This is probably what you want and you should hold a strong reference to this transaction instead of the object/MOC. It's called "unsafe" because of the very nature that you can pass it across scopes, threads, and queues.@markkrenek commented on GitHub (Jan 8, 2016):
This is similar to my question in #34. I was used to passing an object around, and then, any code that needed to modify that object would do something like this:
to guarantee thread/context safety.
I think the suggestion with CoreStore is to stop thinking about MOCs and use a transaction instead. AsynchronousDataTransaction and SynchronousDataTransaction are short-lived, available only during the closures of
beginAsynchronous(_:)andbeginSynchronous(_:). Longer-lived scenarios (which I think I want) need to use UnsafeDataTransaction, but it's still a bit unclear if I'm doing those completely safely.@mantas commented on GitHub (Jan 8, 2016):
@markkrenek looks like we're looking for the same bit :)
@JohnEstropia I'm aware of the CoreStore-y way to solve this. But, wether to my bad habits or app design, I still need to pass around the object itself. At the moment, I work around it by passing around both the object and the transaction. This is quite messy and easy to mess up.
Anyhow, my question is wether there're any purely technical reasons to keep NSManagedObjectContext#parentTransaction method as internal. I'd love to have it as a public method.
@JohnEstropia commented on GitHub (Jan 8, 2016):
@mantas The transactions own the MOCs, not the other way around. MOCs only keep a
weakreference to their parent for things like asserting; it's not meant for any functional purpose. So you will still need to keep a strong reference to the transaction anyway.Notice that if you use raw Core Data, this is still the exact same status quo with NSManagedObject and NSManagedObjectContext: the former only keeps an
unowned(unsafe)reference to the latter, and you'll need to have a strong reference of the MOC somewhere else.@mantas commented on GitHub (Jan 8, 2016):
@JohnEstropia I'm aware of that. But I prefer to have the strong reference somewhere up in the parent and pass down the object only, instead of passing down both transaction and object. That works pretty well with pure CoreData.
Anyhow, would you be interested in making that parentTransaction method public? That's cool if you prefer to keep it internal for one reason or another.
@JohnEstropia commented on GitHub (Jan 8, 2016):
@mantas Would it work for your use-case if I expose a
var parentTransaction: BaseDataTransaction?property from the NSManagedObject instead? (I really don't prefer doing things via MOCs directly)And another thing,
BaseDataTransactiondoes not have its owncommit()method. You'll either have to cast to the specific transaction type or to do commits from the original transaction reference.@mantas commented on GitHub (Jan 8, 2016):
@JohnEstropia that'd be great. Exposing it on NSManagedObjectContext might be more flexible though.
@JohnEstropia commented on GitHub (Jan 10, 2016):
@mantas Can you elaborate how it can be more flexible? Using MOCs is just trying to fight CoreStore's framework design, which tries to prevent MOC access. If there's a functionality that MOCs can do that transactions can't, please tell so I can add methods to the transactions.
@mantas commented on GitHub (Jan 10, 2016):
@JohnEstropia I'm talking about purely hypothetical case there :)
Let's say someone needs to create/fetch object without CoreStore. Wether pure CoreData or some other library. I assume CoreStore's transaction wouldn't be possible on that NSManagedObject. But someone might create a transaction and then do what he needs using context wrapped in that transaction. NSManagedObject still wouldn't have the transaction, but NSManagedObjectContext would.
@JohnEstropia commented on GitHub (Jan 14, 2016):
@mantas I pushed an update, but do tell if you hit a use case where the solution provided was not enough.
As long as an object was created from an
NSManagedObjectContextmanaged by anUnsafeDataTransactionand that the transaction is not yet released, you are guaranteed thatobject.unsafeDataTransactionwill not returnnil.@mantas commented on GitHub (Jan 14, 2016):
thank you!
@mantas commented on GitHub (Jan 17, 2016):
@JohnEstropia looks like I bumped into a very similar issue once again.
I've an object "page" and it has has_many relationship to "section". I want to have a methods Page#custom_get_sections. It'd be more or less an enhanced Page#sections with custom ordering/filtering/whatever. With CoreData, I'd get self.managedObjectContext and fetch sections using the same context as the parent page. However, that doesn't work with CoreStore.
@JohnEstropia commented on GitHub (Jan 18, 2016):
@mantas You should be able to fetch using the transaction:
@mantas commented on GitHub (Jan 18, 2016):
@JohnEstropia what if I'd do smth like this:
It looks like unsafeDataTransaction is not available then
@JohnEstropia commented on GitHub (Jan 19, 2016):
@mantas There is no "transaction" in that case. The fetch is done from a
DataStack. So I think what you wanted to do is to converge these two operationsinto one
for convenience.
I know this should be easy (just expose the internal MOC methods as public), but I really think this is against what CoreStore is trying to do:
Another thing, exposing fetch() methods on MOC is inconsistent API-wise; the behavior is undefined on MOC's not managed by CoreStore.
My suggestion is to either:
custom_children_fetcher()as extension methods on bothDataStackandBaseDataTransactionthat receives theobjectas argument. ORFetchClause(this is how we normally approach similar cases in our projects)Usage: