Okay, PLEASE bear with my description of my situation:
I have a Core Data model that (in a simplified description) includes a GridManager object. The GridManager holds a set of Grid objects (via a to-many relationship). The Grid object contains a set of Cell objects (via a to-many relationship).
In my app, I have a GridView that contains a series of sub-views (of type CellView). The GridView has a representedGrid property and the CellView has a representedCell property (both nonatomic, retain). In the setRepresentedGrid method of GridView, I set the representedCell property of each CellView (subviews of the GridView) to one of the cells in the representedGrid.
NOW, I have two questions:
First, since the cells and the grid are Managed objects, do I still need to release the representedGrid and representedCell properties of the GridView and CellView classes when they dealoc? I figure I do (just as with any retained property), but at one point I thought this was causing a problem with my app--hmmm... just thought, since I write my own setters, and I don't actually retain the grid/cell, perhaps I DON'T need to release them?
Second, only one grid from the gridManager is active at a time. When I switch the gridView.representedGrid from one grid to another, how do I "release" the first grid (and it's associated cells) so that it isn't needlessly taking up memory (given that we're talking about managed objects).
Thanks SO much!
As I understand it you should and can avoid custom getters and setters and then you can leave core data to do it's thing and not worry about retain/release.
As for reducing memory overhead you can ask core data to turn your object to fault using this method: refreshObject:mergeChanges:
Check out the Apple documentation here: http://gemma.apple.com/mac/library/documentation/Cocoa/Conceptual/CoreData/index.html. It's all there and hopefully I've been able to give you the right terms to look for.
Hope that's some help.
If your setters are retaining the managed objects then you need to release them to match the retain/release. However you may not need to retain the managed objects depending on how the application is designed.
Without seeing all of the code it is difficult to give solid advice but you can use the analyzer to check for leaks, use instruments to make sure your memory is not increasing out of control, etc. And last but not least you can turn off retains, switch them to assigns and see if it crashes. Since the NSManagedObjectContext will retain the objects there is a fair chance that your views do not need to retain the NSManagedObject instances at all.
Yes, you need to release your representedGrid and representedCell properties in your dealloc method. If you didn't, the retain/release methods would not be balanced- your setter would be retaining the object, and with no corresponding release, the object will never be deallocated.
When written properly, a retain setter will release its old value and retain the new value. So there is no need to release the old grid when setting gridView.representedGrid.
Why are you writing your own setters, out of curiosity?
Related
I have a quick question about ARC in iOS. (Sorry I've asked so many of these types of questions, but I'm just sooo confused regarding memory management.). It's important to note that I've never used the old memory maintenance system (retain, release, assign...etc) so I don't really know what those terms mean.
Right now I'm confused regarding what I have to do to make sure that strong properties get released properly. For example, suppose I'm making a school app and my School object contains strong property references to 5 different Child objects (not in an array). Each Child object has a strong pointer (property) to a Book object.
If I remove one of the Child objects from my school (say by making its property = nil, or by changing my property to point at a new object), will its Book be properly released? What do I have to do to make sure that this is the case? Do I need to write self.myBook = nil in a dealloc method? What if Child was a View Controller, would I need to write self.myBook = nil in the viewDidUnload method?
I'm targeting only iOS 5 (and up) so the old way of memory management doesn't really matter to me.
If I remove one of the Child objects from my school (say by making its property = nil, or by changing my property to point at a new object), will its Book be properly released?
Yes, it will be released as long as there are no other strong references to it.
What do I have to do to make sure that this is the case?
Nothing in particular: ARC will decrement object's reference count when you set the reference to that object to nil, see that the object is no longer referenced, and proceed to deleting it. It is smart enough to deal with the items referenced from the object being deleted, recursively, so you are not going to leak any memory.
One thing you have to worry about is circular references: if your Book has a strong back-reference to Child, either make that reference weak, or clear it out at the same time as you set your reference of Book to nil (the second option is error-prone, and therefore is not recommended).
Let's suppose I create a few objects and I add them to an array.
House *myCrib = [House house];
House *johnHome = [House house];
House *lisaHome = [House house];
House *whiteHouse = [House house];
NSArray *houses = [NSArray arrayWithObjects: myCrib, johnHome, lisaHome, whiteHouse, nil];
Normally, all House objects have a retain count of two, but they're being autoreleased once. After a while, I decide to release myCrib, even if I'm not the owner — I never retained or initialized.
[myCrib release];
The retain count should drop to zero and my object should be deallocated. My question now is: will this illegal action cause my app to work erroneously or even crash, or will NSArray simply delete my object from its list with bad consequences.
I'm looking for a way to maintain a list of objects, but I want the list to maintain itself. When some object disappears, I want the reference to it to disappear from my array gracefully and automatically. I'm thinking of subclassing or wrapping NSArray.
Thank you.
My question now is: will this illegal
action cause my app to work
erroneously or even crash, or will
NSArray simply delete my object from
its list with bad consequences.
Your array now has an invalid object pointer. There's no way to tell that the pointer is invalid just by looking at it, and the array isn't notified that the object has been deallocated. The problem isn't with the array, after all, the problem is with the code that improperly releases the object. So yes, the application will likely crash or otherwise behave incorrectly due to that bad pointer, and no, NSArray won't detect and deal with the problem for you.
I'm looking for a way to maintain a
list of objects, but I want the list
to maintain itself. When some object
disappears, I want the reference to it
to disappear from my array gracefully
and automatically.
If the objects in the list are all instances of a common class, you could define your own memory management methods that both retain/release the object and add/remove it from the list, or broadcast appropriate notifications in case there can be multiple lists. I suppose you could even override -retain and -release for this purpose, but I'd think long and hard about that before doing it, and document it well if you do; it's not the sort of thing that other developers would expect.
Another option might be Core Data. If you delete a managed object from the object graph, it'll disappear from any relationships. Strictly speaking, a to-many relationship is a set, not a list, but the difference may not be a concern for your purposes.
Update: I just noticed that you didn't tag your question ios. If you're working under MacOS X, you should definitely take a look at NSPointerArray. If you use garbage collection, NSPointerArray can be configured to use weak references and to replace references to collected objects with null references. This is exactly what you seem to be looking for.
You should not release myCrib if you are not the owner. To do so is a violation of the memory management guidelines and will make your code extremely difficult to maintain. I cannot stress enough that you absolutely should never do this under any sort of circumstance. You're asking for crashes; the array has declared ownership of the object, and you must not subvert that ownership in any way.
So the answer here is: your code is absolutely wrong and you should fix it. If you can't fix it, you should trash it and start over and keep rewriting it until you've come up with another way to achieve the same effect without subverting object ownership. I guarantee that it's possible.
If what you want is a weak-referencing array, then there are a couple ways you can do this (this was just asked a couple of days ago):
NSPointerArray - weakly references its pointers. When you use garbage collection, they're autozeroing (ie, the pointers get removed when the object is deallocated). Unfortunately, this is not available on iOS.
CFMutableArrayRef - you can specify a custom retain and release callback, or just not specify one at all. If you leave them out, the array will simply not retain the objects it contains. However, this does not automatically remove the pointer when the object is deallocated.
DDAutozeroingArray - an NSMutableArray subclass I wrote the other day to provide a weakly-referencing and auto-zeroing array that works on both Mac OS and iOS. However, I strongly encourage you to use this only as a last resort; There are probably much better ways of doing what you're looking for. https://github.com/davedelong/Demos
I'm looking for a way to maintain a
list of objects, but I want the list
to maintain itself. When some object
disappears, I want the reference to it
to disappear from my array gracefully
and automatically. I'm thinking of
subclassing or wrapping NSArray.
If I have understood right, what you want is an array of weak references. Then, you might be interested in reading this post.
You're asking for a crash here. Your NSArray will still have a reference to the object that now no longer exists -- and who knows what it will be pointing to after a while?
Subclassing NSArray might not be the answer either. It's a class cluster which, in short, means that it's harder to subclass than you might hope.
Not entirely sure how you'd implement this. Something like the element sending a notification when they're about to be deallocated which the array would then pick up. You'd need to be careful that you didn't leak or over-release your objects.
I created a wrapper class — in my code it's called a controller — which maintains the (mutable) array for me. I initialize the controller class in my view controllers — the place where I need them — instead of using an array directly.
No invalid code for me. :-p
In a project with garbage collection set to required: If I instantiate an object in Interface Builder and add it to the XIB/NIB, do I need to assign this object to some outlet to avoid it being garbage-collected, or is that taken care through some other means?
Top level objects need to be assigned to an outlet somewhere or attached to some variable using the top level objects array when instantiating the nib. Otherwise, they are useless to you anyway. A view which is contained within another view or window is in that views instance variables so it is safe.
I just built a small test project. With GC, the object created through Interface Builder gets collected soon afterwards. So the answer to the question is: yes.
I did a bit more testing:
With classical reference counting this does not happen. This is possibly what one would expect, but it is also probably a memory leak. Not sure here. (If not, and the NIB/XIB-contained objects get released when the file's owner is release, e.g., then this would constitute a significant difference in behavior between GC and classical RC.)
With ARC enabled, I did not find a way to tell. There is probably some function I could put a breakpoint on and wait for the specific object fly by.
Edit:
I reviewed the documentation, and it states it quite clearly. Missed that the first time around:
You typically need strong references to top-level objects to ensure that they are not deallocated; you don’t need strong references to objects lower down in the graph because they’re owned by their parents, and you should minimize the risk of creating strong reference cycles.
From: Resource Programming Guide, Managing the Lifetimes of Objects from Nib Files
I am smitten by KVC/KVO. Super powerful. There is one problem though. I'm trying to be true the the MVC etho but I see no way to use an observation pattern to monitor the allocation or deallocation of an Objective-C class instance.
This is actually important as I have a model with fine-grained internal messaging that I want to observe from a controller (or delegate). The stumbling block for me is I don't see how, external to the model, I can remove an observer for a sub-component that is about to be deallocated without the controller knowing about the internal logic of the model which would compromise encapsulation.
Can someone suggest an approach for this scenario.
Thanks,
Doug
Doug - there really isn't enough information in your description to know what it is you are doing and how to best (or if it is appropriate at all) apply KVO to the problem.
KVO is all about observing properties on objects. You typically shouldn't care when they are created or destroyed except insofar as you must stop observing them before they are destroyed.
You should instead start and stop observing objects when those objects become interesting to you. Consider a graphics drawing package where a document has an ordered array of shapes, and you are interested in observing the backgroundColor property of each shape.
We wouldn't try to observe the instantiation and deallocation of the Shape instance, but instead we observe the "shapes" property on the document. Through that observer, we can determine when a shape is added to, or removed from, the document. When a shape is added to the document, we start observing it. When it is removed from the document, we stop observing it. (Note that it may be removed from the document but not deallocated, if it is on the undo stack, etc.)
In the object graph for your model, to use KVO you'll want to add and remove the objects from your object graph in a KVO compliant way so you can observe the relationship mutations, and in that observer, start and stop property observers on the related objects.
I think you'll have to post the notifications yourself, unless you use something like CoreData. If you're using CoreData, NSManagedObject (the root class of all stored CoreData objects) has an -awakeFromInsert method that gets called after the object has been created and inserted into the ManagedObjectContext.
As for destruction, you could probably just post a notification right as you enter the -dealloc method.
I'm not sure exactly what you're trying to achieve, so a little more explanation would be good.
If you just want to remove an observer before the observed object gets deallocated, then don't worry, because KVO will handle it. Even if you're using notifications it won't cause a problem, you just won't receive any notifications from the object.
If you're trying to observe multiple objects (e.g. an array of Widgets), and would like to know when an object is added or deleted, KVO can handle that too. You just have to make the array a key on your model object, and observe it with KVO. You also have to modify the array in a KVO compliant way (e.g. mutableArrayForKey:, or use your own willChangeValueForKey and didChangeValueForKey).
I want to cache the instances of a certain class. The class keeps a dictionary of all its instances and when somebody requests a new instance, the class tries to satisfy the request from the cache first. There is a small problem with memory management though: The dictionary cache retains the inserted objects, so that they never get deallocated. I do want them to get deallocated, so that I had to overload the release method and when the retain count drops to one, I can remove the instance from cache and let it get deallocated.
This works, but I am not comfortable mucking around the release method and find the solution overly complicated. I thought I could use some hashing class that does not retain the objects it stores. Is there such? The idea is that when the last user of a certain instance releases it, the instance would automatically disappear from the cache.
NSHashTable seems to be what I am looking for, but the documentation talks about “supporting weak relationships in a garbage-collected environment.” Does it also work without garbage collection?
Clarification: I cannot afford to keep the instances in memory unless somebody really needs them, that is why I want to purge the instance from the cache when the last “real” user releases it.
Better solution: This was on the iPhone, I wanted to cache some textures and on the other hand I wanted to free them from memory as soon as the last real holder released them. The easier way to code this is through another class (let’s call it TextureManager). This class manages the texture instances and caches them, so that subsequent calls for texture with the same name are served from the cache. There is no need to purge the cache immediately as the last user releases the texture. We can simply keep the texture cached in memory and when the device gets short on memory, we receive the low memory warning and can purge the cache. This is a better solution, because the caching stuff does not pollute the Texture class, we do not have to mess with release and there is even a higher chance for cache hits. The TextureManager can be abstracted into a ResourceManager, so that it can cache other data, not only textures.
Yes, you can use an NSHashTable to build what is essentially a non-retaining dictionary. Alternatively, you can call CFDictionaryCreate with NULL for release and retain callbacks. You can then simply typecast the result to a NSDictionary thanks to tollfree bridging, and use it just like a normal NSDictionary except for not fiddling with retain counts.
If you do this the dictionary will not automatically zero the reference, you will need to make sure to remove it when you dealloc an instance.
What you want is a zeroing weak reference (it's not a "Graal of cache managing algorithms", it's a well known pattern). The problem is that Objective C provides you with zeroing weak references only when running with garbage collection, not in manual memory managed programs. And the iPhone does not provide garbage collection (yet).
All the answers so far seem to point you to half-solutions.
Using a non-reataining reference is not sufficient because you will need to zero it out (or remove the entry from the dictionary) when the referenced object is deallocated. However this must be done BEFORE the -dealloc method of that object is called otherwise the very existence of the cache expose you to the risk that the object is resurrected. The way to do this is to dynamically subclass the object when you create the weak reference and, in the dynamically created subclass, override -release to use a lock and -dealloc to zero out the weak reference(s).
This works in general but it fails miserably for toll-free bridged Core Foundation objects. Unfortunately the only solution, if you need to to extend the technique to toll-free bridged objects, requires some hacking and undocumented stuff (see here for code and explanations) and is therefore not usable for iOS or programs that you want to sell on the Mac App Store.
If you need to sell on the Apple stores and must therefore avoid undocumented stuff, your best alternative is to implement locked access to a retaining cache and then scavenge it for references with a current -retainCount value of 1 when you want to release memory. As long as all accesses to the cache are done with the lock held, if you observe a count of 1 while holding the lock you know that there's no-one that can resurrect the object if you remove it from the cache (and therefore release it) before relinquishing the lock.
For iOS you can use UIApplicationDidReceiveMemoryWarningNotification to trigger the scavenging. On the mac you need to implement your own logic: maybe just a periodical check or even simply a periodical scavenging (both solutions would also work on iOS).
I've just implemented this kind of thing by using an NSMutableDictionary and registering for UIApplicationDidReceiveMemoryWarningNotification. On a memory warning I remove anything from the dictionary with a retainCount of 1...
Use [NSValue valueWithNonretainedObject:] to wrap the instance in an NSValue and put that in the dictionary. In the instance dealloc method, remove the corresponding entry from the dictionary. No messing with retain.
My understanding is that you want to implement the Graal of cache managing algorithms: drop items that will no longer be used.
You may want to consider other criteria, such as dropping the least recently requested items.
I think the way I would approach this is to maintain a separate count or a flag somewhere to indicate if the object in the cache is being used or not. You could then check this when you're done with an object, or just run a check every n seconds to see if it needs to be released or not.
I would avoid any solution involving releasing the object before removing it from the dictionary (using NSValue's valueWithNonretainedObject: would be another way to accomplish this). It would just cause you problems in the long run.