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.
Related
How can I know when to trust my NSManagedObject with stay around with its non managed variables. Surely a strong reference?
A managed object being a fault is unrelated to whether you have a strong reference to it. The comparison is sort of like saying, if I have a strong reference to an object, can its property values change? So yeah, it's possible for a managed object to re-fault regardless of what kind of reference(s) you have to it.
As a specific example, if you call refreshObject:mergeChanges: with your object as the first argument and with the second argument set to NO, you force the object to re-fault. But you can still have a strong reference to it.
But keep in mind that being a fault object has no relationship to whether it will "stay around", as you say. It still exists in memory. It hasn't been deallocated. You can even still use its property values.
I have a collection which maintains weak references to its objects. I'd like it to conform to NSFastEnumeration, but the buffer provided by countByEnumeratingWithState:objects:count: uses unsafe_unretained references. That creates a gap during which a returned reference could become invalid but not zeroed.
That's fine in the general case -- if the collection stuffs its (currently valid but weakly-referenced) object into the buffer and returns it, then the caller will presumably create its own strong reference if needed. But that leaves two problems:
(1) I don't see any guarantee that the for(){} iteration construct itself creates a temporary strong reference to the object, so if the contents of the {x} block changes something outside the collection in a way that causes the object to be released, then it'll have a dangling reference.
(2) There's still a small gap while returning from countByEnumeratingWithState: during which activity on another thread could invalidate the reference. My collection isn't meant to be thread-safe, but it would be nice if it could at least safely store references to objects which could be referenced on another thread, as there's really no way to prevent that in any multi-threaded application.
You can't return a strong reference directly to the caller. The caller won't release it, and the fast enumeration protocol does not guarantee that you will get a chance to release it yourself when the caller is done.
Instead you can retain+autorelease the objects before you store them into the buffer. That would guarantee the objects stay alive while the caller uses them. It may hurt the "fast" part of fast enumeration, but you would still get the "convenient syntax" part. If you add a nil check after you read the weak variable then you can avoid storing nil pointers into the buffer.
In the book Learning Core Data for iOS, the author creates several UIViewControllers which each have a property that refers to an NSManagedObjectID.
In example,
#interface LocationAtShopViewController : UIViewController
#property (strong, nonatomic) NSManagedObjectID *selectedObjectID;
// ... other properties and methods
#end
In this manner, he is able to pass an NSManagedObjectID from one controller to another and retrieve the associated NSManagedObject object using NSManagedObjectContext's existingObjectWithID:error: method.
Further, he doesn't ever set an NSManagedObject object directly (even if he already has a variable reference to it), nor does he keep a reference to the NSManagedObject object very long (instead, he retrieves it in each method that he needs it).
Is it unsafe (i.e. will cause crashes or lead to unexpected behavior in certain circumstances) to pass an NSManagedObject directly between controllers via a property reference, or simply keep a reference to it on a controller?
In example,
#interface LocationAtShopViewController : UIViewController
#property (strong, nonatomic) LocationAtShop *locationAtShop;
// ... other properties and methods
#end
Assuming a single, shared NSManagedObjectContext is used, so disregard issues caused by passing between multiple contexts, which isn't safe in general.
There is no reason to avoid using the managed object directly, provided that:
You only use the managed object with a single managed object context, and
You either
only ever use the managed object on a single thread or queue, or
Make sure to use performBlock or performBlockAndWait when working on a different queue.
Keeping only the object ID may be less error-prone since it makes it a lot harder to accidentally mix up contexts or queues. That may make it a better idea for less experienced developers, who are therefore less likely to screw things up. But it's certainly not wrong nor even especially dangerous to keep the object itself.
Is it safe ?
Yes, it's safe, with some caveats:
Per Tom Harrington's answer, in general it's safe to store an NSManagedObject as a property on a controller.
However, there are situations in which this can cause problems. Notably,
If the referenced NSManagedObject is deleted and its context is saved, the property must explicitly be set to nil.
If you do not explicitly set the property to nil, the next time you try to access an attribute on the object, it will cause a CoreData could not fulfill a fault crash.
Why did the author of Learning Core Data for iOS pass and store the NSManagedObjectID instead of the NSManagedObject?
The likely reasons may include:
If instead of storing the NSManagedObject as a property you stored the NSManagedObjectID, you don't have to worry too much about the object being deleted.
This is because NSManagedObjectContext's method existingObjectWithID:error: will return nil in the event that the object cannot be fetched, or does not exist, or cannot be faulted.
So, it's safer to store the NSManagedObjectID as a property than it is to store the NSManagedObject, if deletion of the object is possible.
I am creating some child context from the main context and I wanted to know do I need to release the child context?
I have a CoreData class which creates child context (this class is an arc enabled classes) but it can provide context to other classes which might not be arc classes.
- (NSManagedObjectContext *) createChildContext
{
NSManagedObjectContext *childManagedContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
childManagedContext.parentContext = [self parentContext];
return childManagedContext;
}
then some other class can get the child context
NSManagedObjectContext *context = [coredata createChildContext];
My question is do I need to release the context in this class or will it be auto managed by the arc class that created it since I am not retaining it.
This is actually a pretty interesting question, since you're mixing ARC and non-ARC. As always, though, the Memory Management Policy can help us decide what to do in this situation.
First off (as I think you already know), you don't have to release the context in the ARC class that creates it (inside -createChildContext). In fact, you can't explicitly release or autorelease it there, since the class is ARC - you can't do any manual memory management inside it.
Now let's look at the calling class. As you have it written, you do not have to release the context once you're done with it. Since you have not taken ownership of the context, you shouldn't tell the object that you're releasing it; it's not your place to give up ownership of an object you never owned in the first place.
Why haven't you taken ownership of the context? Well, as you have it written, you never:
Explicitly retained the object (e.g. [[coredata createChildContext] retain])
Assigned the object to a #property marked retain or strong
Got the object from a method starting with alloc, new, or copy
In this sense, you could think of the object being "managed by" the ARC class that created it, though that's a bit misleading; in reality, it doesn't matter who manages it, it just matters that you don't own it in the non-ARC calling class.
If you need to change that for some reason - say, you need the child context to stick around for a while, or you intend to use it throughout your class - you could consider one of the approaches listed above, where you do take ownership of the context. In that case, you would have to release it when you're done with it.
I highly recommend reading over the Advanced Memory Management Programming Guide; it has a lot of very useful things to say about dealing with object ownership, among other topics.
I think I have a problem, maybe linked to a retain cycle in Core-Data.
The code is a follow, where self.image is also a NSManagedObject:
- (void)setImage:(UIImage*)image1 andThumbnail:(UIImage*)image2
{
self.image.data = UIImageJPEGRepresentation(image1, 0.85); // This is autoreleased
self.thumbnail = UIImageJPEGRepresentation(image2, 0.85); // This is autoreleased
}
Apparently, the "self.image.date =" has one retain that is never released (and I think that it is between self.image and self). Because of that the self object will never be released and hence the leak.
EDIT: so basically I have the same problem as here: https://devforums.apple.com/message/246219#246219
I use exactly the same structure where the self in the previous code corresponds to the Bar in the given link. I also have the same view controller structure. However, the refreshObject doesn't help.
I tried to use the NSManagedObjectContext refreshObject method to break the retain cycle (as suggested in Apple documentation). It has no influence on the retainCount. I'm probably not using it the right way but I can't find much information about it. If I use NSManagedObjectContext:reset: I get a crash in the root view controller when I come back to it.
Thanks!
You should not interfere with the managed object context's management of the managed objects memory.
If self.image above is a managed object and you have not written custom accessors then you have no memory management concerns from it. Any attempt to manage the context's memory manually will almost always cause more problems than it solves.
Retain counts tell you nothing except in the simplest and smallest of command line apps. Once you use frameworks like Core Data, the behind the scenes retention is so complex that the retain count often bears no relationship to what happens in your own code.
Apparently, the "self.image.date ="
has one retain that is never released
(and I think that it is between
self.image and self). Because of that
the self object will never be released
and hence the leak.
This will not happen. You do not have to kill all objects in an instance's retained attributes before killing the instance itself. If that was true, you couldn't kill an instance that shared an attribute object with a 3rd object. If they were non-managedObject instances, the self.image object can exist long after the self object dies. Only the enforcement of the entity graph by the context makes them behave different and that has nothing to do with memory management.
If you see a mysterious retain count of 1 on a managed object, that is the retain put on the object by the managed object context. As long as the context believes the managed object must exist in the entity graph, it will never release the object.
If the leak is in the Core Data stack at all, your problem is most likely in the entity graph between the self entity and the self.image entity. The entity graph is preventing one or the other from being deleted most likely by a deny or a required relationship.