For an object shared between threads (via persisting and querying), will changes to an ignored property made in one thread be visible in another thread?
To share objects between threads or re-use them between app launches you must persist them to a Realm ... all changes you make to it will be persisted (and must be made within a write transaction). Any changes are made available to other threads that use the same Realm when the write transaction is committed.
http://realm.io/docs/cocoa/0.91.1/#writes
It looks like this doesn't apply to ignored properties. Each thread's instance of the object has its own copy of the ignored property, and changes in one thread don't affect any other threads. Is that right?
Correct. When you access an RLMObject from another thread by re-querying for it, it will be a new instance of the object, so the ignored properties will not be carried along with that one.
That being said, as long as you don't try and access any of the Realm-backed properties (Else a RLMException will be triggered), you CAN pass an RLMObject instance from one thread to another and still continue to access its ignored properties on the new thread.
Related
When i was learning how to use child contexts :
let childContext =
NSManagedObjectContext(
concurrencyType: .mainQueueConcurrencyType)
childContext.parent = coreDataStack.mainContext
let childEntry =
childContext.object(with: surfJournalEntry.objectID)
as? JournalEntry
// 3
detailViewController.journalEntry = childEntry
detailViewController.context = childContext
detailViewController.delegate = self
Author made some remark about passing both managed object and the managed object context to the detailViewController:
Note: You might be wondering why you need to pass both the managed
object and the managed object context to the detailViewController,
since managed objects already have a context variable. This is because
managed objects only have a weak reference to the context. If you
don’t pass the context, ARC will remove the context from memory (since
nothing else is retaining it) and the app will not behave as you
expect.
Well, ok, so then a read some official doc:
This means that in general you cannot rely on a context to ensure the
longevity of a managed object instance, and you cannot rely on the
existence of a managed object to ensure the longevity of a context.
Put another way, just because you fetched an object doesn’t mean it
will stay around.
But yet, i don't get what is true intention of making weak references between managed objects and the context? What is the goal do they pursue?
Managed object contexts usually use weak references to fetched objects to avoid the potential for excessive memory use. If it used strong references, and you did one or more fetches that found a large number of results, all of them would remain in memory for as long as the context existed. In many apps that would mean they'd never go away, because the context exists until the app exits. That could cause the app to use a lot of memory for objects it wasn't using anymore. Weak references mean that the managed objects are deallocated as soon as the app stops using them.
But you might want strong references in some cases, so there's a boolean property called retainsRegisteredObjects that makes the context use strong references instead. Use it if you like, but be careful of memory use.
Managed objects don't keep strong references to their contexts to avoid reference cycles. If they were strong references and you set retainsRegisteredObjects to true, you'd get a reference cycle. Each object would hold a strong reference to the other, so neither could be released from memory unless you set one of the references to nil.
You can think of managedObject as small and stupid object. They have a pointer to their context and know their objectId. When they need to know something they query the context. This has a lot of really neat advantages. If an entity was already queried in the context, a second instance of it will hit the row cache in the context and not hit the store at all.
Generally there are two kinds of context (for most core data setups): long lived main queue context that are always in memory, and short lived background contexts. For the main queue context you generally don't need to worry about the contexts leaving memory. They stay in memory for the lifetime of the application. The short lived context have a problem of leaving memory and also have a problem that they are not thread-safe. So generally they should be created in a block that is on the correct thread - used and then discarded and not pass out of the block.
I hope that explains it.
My parent object creates a child object to do some async action. It involves some user interaction and that's one of the reasons I didn't use NSOperation. Only the child object knows when it has finished its task, then how does it tell its parent object that I am done, please release me to avoid memory leaks?
Currently, I come up with this, the parent object has a callback method called releaseChild, which is just child = nil; When the child object finishes its task it calls parent's releaseChild just to set itself to nil. Actually, I wrap the call in a dispatch_after but the idea is the same, the child object calls its parent to set itself to nil.
I was wondering is there a better way ?
----- update -----
I know the "normal" way to write my child is to create a UIViewController object, push it to UINavigationController. When it is finished user can pop it up, ARC will then release it. In that way, I don't need to worry about memory leaks (normally). But I think the question to release a child object when it is done and only it knows when it is done has a border application than writing a UIViewController object.
In the question:
Only the child object knows when it has finished its task, then how does it tell its parent object that I am done, please release me to avoid memory leaks?
and in a later comment:
the problem is the child object can't set itself to nil.
What it appears you need is a child object which controls its own lifetime. This is easily done; an object remains alive as long as there is a reference to it stored in a variable attributed as asserting strong ownership over references stored in it - and that attribute is the default for variable declarations. That variable can be global, local, instance, it doesn't matter... So to control your its own destiny an object just needs to keep a reference to itself.
The following code, copied from Manual object lifetime with ARC (a previous answer I wrote a few years ago, I can crib from myself ;-)), shows one way to do this:
#implementation MasterOfMyOwnDestiny
{
MasterOfMyOwnDestiny *alsoMe;
}
- (void) lifeIsGood
{
alsoMe = self;
}
- (void) woeIsMe
{
alsoMe = nil;
}
...
#end
In your case you can have the child set alsoMe during its initialisation, so the child starts off controlling its own lifetime. When the child decides it has finished its work it simply calls [self woeIsMe] as its final action.
Note: You can of course remove the methods and just set the instance variable directly within the child, making it hard for any other object to kill the child off.
If the parent needs to have a reference to the child for any reason, other than to keep the child alive, then it can store that reference in a variable attributed as asserting weak ownership of references stored in it, e.g. something like:
__weak MasterOfMyOwnDestiny *myChild = [MasterOfMyOwnDestiny new];
such a reference will not keep the object referenced by myChild alive, and will be automatically set to nil when that object dies.
Couple of possible solutions
1) Use protocol-delegation method to let child tell parent that its done (i am assuming thats what you are doing)
2) you can pass variable into the child and which gets set by child once its finishes execution and parent can periodically check its value and clear the child (not a big fan, but kind of polling method), Can be done with created background task or can be added to low priority queue.
3) Use NSNotificationCenter to send notification to parent and tell him works done. You can add userinfo to identify children among each other.
4) If you don't need to do anything specific after work is done just use dispatch_async and don't hold the reference, it will do its task and die down eventually
I would require more details to give you recommendation about any specific approach. If possible paste your code :)
If a child object performs an asynchronous operation, it would be most likely that it has a callback block that it calls when the operation is finished, either with success or with failure. And most likely the parent object has created the child, stored a reference, told the child to start the asynchronous operation, and given it the callback block. So that callback would be the perfect place to set some reference to nil.
We are experiencing the following crash
realm::Realm::verify_thread() const (shared_realm.cpp:274)
It happens sporadic but from different flows in our code.
One of the stacktraces we find is
0x00000001003af7ec realm::Realm::verify_thread() const (shared_realm.cpp:274)
0x0000000100339d78 RLMGetObjects (RLMObjectStore.mm:83)
0x0000000100330130 +[RLMObject objectsWithPredicate:] (RLMObject.mm:150)
0x00000001000fa468 -[PrinterRepository getDefaultPrinter] (PrinterRepository.m:35)
0x00000001001faf3c -[PrintService handlePrintJobs] (PrintService.m:106)
Our code in [PrinterRepository getDefaultPrinter] is
return [[Printer objectsWithPredicate:[NSPredicate predicateWithFormat:#"isDefault == 1"]] firstObject];
Locally we aren't able to reproduce this yet, we've only seen this happen from time to time with our beta testers.
Our realm version is 0.102.1
Our iOS versions are 9.2.1, 9.3.2 & 9.3.3
Does someone has an idea of the cause of this crash?
Managed Realm objects are thread confined. You're not allowed to pass them around arbitrarily between threads. When you retrieve an object on the main thread, you're only allowed to use it there. When you want to pass it to a background thread, you will need to retrieve on the main thread an identifier, ideally the property designated as primaryKey, and pass that over to the background thread.
You get a failure like that whenever you violate against that.
See our the relevant chapter of our docs about "Passing Instances Across Threads":
Unmanaged instances of RLMObjects behave exactly as regular NSObject subclasses, and are safe to pass across threads.
Instances of RLMRealm, RLMResults, or RLMArray, or managed instances of RLMObject can only be used on the thread on which they were created, otherwise an exception is thrown*. This is one way Realm enforces transaction version isolation. Otherwise, it would be impossible to determine what should be done when an object is passed between threads at different transaction versions without a potentially extensive relationship graph.
Instead, there are several ways to represent instances in ways that can be safely passed between threads. For example, an object with a primary key can be represented by its primary key’s value; or an RLMResults can be represented by its NSPredicate or query string; or an RLMRealm can be represented by its RLMRealmConfiguration. The target thread can then re-fetch the RLMRealm, RLMObject, RLMResults, or RLMArray using its thread-safe representation. Keep in mind that re-fetching will retrieve an instance at the version of the target thread, which may differ from the originating thread.
I understand why this error happens: when you try to access a CoreData object that was deleted in a managed object context on another thread, and is hence set to a 'fault' object, and any retained references will therefore no longer point to a valid CoreData object.
I am using a NSFetchedResultsController.
I have confirmed that all the code is implemented correctly. I have 2 managed object contexts, one intended for a BG thread and one for the main thread.
I have confirmed that the main thread is subscribed for notifications under NSManagedObjectContextDidSaveNotification.
I have confirmed that when this notification fires, I perform a mergeChangesFromContextDidSaveNotification: on the main thread managed object context.
I am NOT retaining these objects anywhere, but I am setting the batch size for the NSFetchRequest (Is this potentially the issue?)
YET, I still occasionally get the 'CoreData could not fulfill a fault' error.
In my particular application, this usually happens during a sort of "data-bind" process so I could safely just discard fault objects and move on. I would like to do this by wrapping the inside of the loop that data-binds in a #try-catch block and just skip rows that I get the CoreData error for.
Can I safely do this with CoreData? Or do I need to completely dump the managed object context after I encounter a fault.
I did check this question about how to check if a CoreData object is a fault, which might be something I implement if I can't safely assume that my #try-catch block won't cause other issues.
Instead of using try-catch we can use "existingOjectWithId" and check for nil on returned object.
- (NSManagedObject*)existingObjectWithID:(NSManagedObjectID*)objectID error:(NSError**)error
The above returns the object for the specified ID if it is already registered in the context, or faults the object into the context. If the object cannot be fetched, or does not exist, or cannot be faulted, it returns nil. Unlike -objectWithID: it never returns a fault.
I have a doubt, Do I need to delete a background thread when the execute is done? Or the thread is automatically clean and deleted?
In Java and Groovy, object instances are automatically garbage-collected when there's no more reference to them.
The Background Thread Plugin doesn't hold references to Runnables or closures you pass to it (let's call them "threads") once those have been executed.
However, you shouldn't hold large arrays of references to these "threads" in instance variables at the class level. (I guess, you wouldn't do, anyway.) Remember that Services in Grails are singleton-scoped and have the same life span as the Grails application.
In contrast, object instances at the method level are garbage-collected after the method call has completed.
After all, there's simply no need to delete "threads", just don't store the them in instance variables.