I have a model some instances of which I need to persist. Only some, not all, since persisting all instances would be wasteful. The model has primaryKey of type Int
I need to be able to pass all objects from background to main thread since Realm objects can only be used by the thread on which they were created. Current version of Realm Swift (0.94) does not seem to support handing the object over to another thread directly.
For persisted objects (the ones saved to storage with write) this is not a problem, I can fetch the object on another thread by primaryKey.
Unpersisted objects, however, are problematic. When I create a new object with the same primaryKey in background (I suppose it should be treated as the same object since it has the same primaryKey) and try to fetch it on the main thread (without persisting changes with write since I don't want in in the storage), I seem to get the old object.
I see the following solutions to this problem:
1) persist all objects (which is undesirable and otherwise unnecessary)
2) keep separate model for the objects I want to persist (leads to code duplication).
3) use init(value: AnyObject) initializer which creates unpersisted object from a Dictionary<String, AnyObject> (would probably require manual copying of object properties to dictionary, tedious and potentially error-prone)
Are there any better solutions?
Offtopic: I haven't tried Realm for Android, is situation any better with it?
You are free to pass unpersisted objects between threads as you wish -- only persisted objects cannot yet be handed over between different threads.
I think your problem is that you are creating two objects that you want to be the same object and there is no way the system can know which one you want.
The solution is as simple as it is generic: create a new object only after checking that its unique attribute does not exist already. This should work equally well with persistent and non persistent objects. Obviously, you need to have a central, thread safe in-memory repository where you can go and create new objects.
You write:
I seem to get the old object.
There should not be any old object if you have checked before.
Related
I was wondering if someone would be able to answer this question. I am currently building my application using the CoreData stack as described by Marcus Zarra in his blog http://martiancraft.com/blog/2015/03/core-data-stack/. He describes the usage of managedObjectContext as the Single Source Of Truth where all insertions/updates/deletions should be done on this context. No exceptions. Curiously then, since this context is a child context to the private context, if I keep inserting new NSManagedObjects into managedObjectContext... wouldn't this context be filled with temporaryObjectIDs since the parent does not refresh the child context? I ask this question because how would I retrieve this NSManagedObject back from the PSC using the NSManagedObjectID if I only have the temporary one? Would I have to explicitly throw out another performBlock using the privateContext to fetch for it? That feels like a very inelegant of a solution.
You can force the conversion to happen early, before you've saved changes, like so:
[moc obtainPermanentIDsForObjects:moc.insertedObjects.allObjects error:&error];
As I understand it, the object is already in both contexts. You don't need to retrieve it from the store, as it's cached.
You still have the object and could query the store, based on the object's attributes. The only need for the ID was if you needed to pass it to another thread, but you only have the one Single Source Of Truth. What other thread is there?
Update:
After discussing your app, I would suggest not to add complexity if there is no need for complexity. You already have an API thread. Don't try to prematurely optimize it by splitting an API task into multithreaded sub tasks. You're adding overhead and the unnecessary burden of passing IDs between private contexts.
Treat what the background API is doing as a single operation, and let it download, insert, save, then notify the SSoT, which can then (re)fetch.
If it is responsive enough, you're done. You have a simple, contained approach, with no need to pass IDs.
In general, don't optimize unless there's a need for it.
I'm making a simple bank account tracker, for self-instructional purposes. I'm using Core Data to store three entities, related as in the screenshot:
WMMGTransaction objects are simply stored as they are recorded, and extracted as needed to feed tableviews and detail views. This will be done via NSFetchedResultsController and a predicate. I'm using MagicalRecord to access Core Data, if that matters.
My question is this:
When I pass WMMGAccount data from one VC to another, such as when creating a new account, or when selecting one from a list (via delegation as a rule), does it matter if I pass a reference to the entire entity, or can I just use an NSString bearing the .name of the account and identify the account when required with a predicate and an NSFetchedResultsController? I guess this is a strategy question, and may generate discussion, rather than having a cut and dried answer, but I'm wrestling with it, so I thought I'd ask.
It sounds like you're asking if you should pass an object to the code that needs it, or if you should pass information that could be used to look up the same object again.
Unless you need to use the managed object on a different thread or queue, you should always pass the actual object. No sense re-fetching an object you already have. It's extra work and code complexity that (unless there are some unusual extenuating details you didn't mention) won't help in any way.
If you are needing to use the object on a different queue or thread, passing information that can be used to look it up is the correct approach. But in that case-- don't pass the value of one of the properties. Use the managed object ID.
Core Data won't force name values to be unique, while the object's managedObjectID is unique. It's also faster when retrieving the object, because you can use objectForID: or existingObjectForID: instead of performing a fetch.
I am using the core data stack shown in the image below. I want to design a structure where objects can be created in both worker contexts.
What I am observing in the setup is if both contexts try to create the same object (for a unique key) at around the same time, db ends up in creating two rows for the table. Is there a way to solve this? Thanks in advance for your response.
The only way you can ensure uniqueness would be to have a coordinating object that all contexts turn to to verify their operation (a "uniqueness enforcer" if you will).
The general algorithm is described HERE, however you fall under the "multi-threaded/context" category and this will complicate things.
In a multi-threaded environment, your enforcer would have to perform a save to the store (using its own managed object context) before returning results to the calling object.
The general flow would be (no cache version):
A context request object for keys from the enforcer
The enforcer issue the request "under lock" (either locking an actual lock or using a serial dispatch queue)
the enforcer query the store for existing objects
create objects for missing keys and save them
you might want to mark the objects as stubs, as the caller might not eventually save and it will give you a flag to ignore them in your fetch requests in your views
build the results array with the objects he created
the results might be NSManagedObjectIDs or imported objects in the caller context otherwise you risk cross context access of managed objects
In my code I do a large import of thousands of objects and execute save after each object .
Do I need to turn each object into faults after saving it to save memory or will Core Data turn it automatically into faults? Unfortunately, I have not found any clue in Apple`s doc.
Thank you!
A. You can turn an object into a fault with refreshObject:mergeChanges: (NSManagedObject). This will give up strong references to related objects, so that they can be released. (If they are not holded by another reference.)
You can turn a realized object into a fault with the
refreshObject:mergeChanges: method. If you pass NO as the mergeChanges
argument, you must be sure that there are no changes to that object’s
relationships. If there are, and you then save the context, you will
introduce referential integrity problems to the persistent store.
(Link)
B. You can wipe all objects out with -reset (NSManagedObjectContext) as Daniel G said. But this really wipes out the objects, references can break.
C. I think, that there is no promise of CD to turn all saved objects into faults. (Unsaved objects cannot turn into faults.) But simply overwrite -didTurnIntoFault (NSManagedObject) to see the behavior of CD.
I'm not exactly sure what you are asking but if you have a lot of objects hanging around in your context you can always use the method -[NSManagedObjectContext reset] to reset the context and purge memory. You also don't want to do this if you have any objects that reference NSManagedObjects within the recently reset context.
I'm not sure as to the nature of your application or why you would need to execute a save on thousands of objects, it seems that you should look into using batches for your fetch requests. This way core data will guarantee that only a specified number of objects will be living in the context at any given time.
I hope this helps?
I am new to db4o.
I have this question in mind:
when the object are retrieved from DAL, maybe it will update in Business layer, then we lost it's original property, so when it comes to updating how can I find which one is the original object in the database to update?
You need to be more precise about "the object". If you modify the object instance's properties, simply storing it again will perform an update:
MyClass someInstance = ObjectContainer.Query<MyClass>().FirstOrDefault();
someInstance.Name = "NewName";
someInstance.PhoneNumber = 12132434;
ObjectContainer.Store(someInstance); // This is the update call
[This is just pseudo-code]
So you don't need to match objects to each other as you would have to when using an RDBMS.
However, you need to make sure you are not using a different instance of ObjectContainer, because a different container will not know these objects are the same instance (since there is no ID field in them).
Your application architecture should help to do this for most workflows, so there should be really only one IObjectContainer around. Only if timespans are really long (e.g. you need to store a reference to the object in a different database and process it somehow) it'd use the UUID. AS you already pointed out, that requires to store the ID somewhere else and hence complexifies your architecture.
If you however intend to create a new object and 'overwrite' the old object, things get somewhat more complicated because of other objects that might refer to it. However, this is a somehwat pathological case and should typically be handled within the domain model itself, e.g. by copying object data from one object to another.
You should load the object via its ID:
objectContainer.get().ext().getByID(id);
or via its UUID:
objectContainer.get().ext().getByUUID(uuId);
See the docs for the latter one. For an explanation see the answer here or the docs here. In short use uuid only for long term referencing.