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).
Related
During the last time I'm trying to become aware of Objective-C memory management and once I asked myself the following question:
Sinceweak reference in Objective-C let us to avoid retain cycles why not to use them all the time?
An object only stays in memory while there is at least one strong (default) reference to it. If you only use weak references, the object is immediately deallocated.
Simply said, "weak" means: This object belongs to someone else, I'm only accessing it and I don't mind if it disappears.
Sinceweak reference in Objective-C let us to avoid retain cycles why not to use them all the time?
Because an object is deallocated as soon as there are no strong references to it.
More importantly, a strong reference is often wanted in order to prevent an object's deallocation. For example, a view controller is ultimately responsible for its view -- it wouldn't make much sense to have the controller's view suddenly just disappear. So UIViewController's view property is strong.
A retain cycle happens when two objects each have strong references to the other. Neither object can be deallocated as long as the other one keeps it's strong reference. This isn't really a problem if you're aware of the situation, because you can break the retain cycle by eliminating either of the references. However, it's easy to not be aware of the cycle, or to forget to deal with it, and then you've got two objects keeping each other in memory beyond their useful lifetime. Making one of those references weak eliminates the problem, but it also means that the referenced object may be deallocated and the reference may become nil at any point. In practice, that's usually not a problem.
Seems Automatic Reference Counting (ARC) puts so much burden on the developer to keep track of references. Why couldn't you just make every variable weak and not have to worry about the strong retain cycles happening at all?
Im afraid you just dont understand ARC. ARC does the counting of references for you, depending on whether something is defined as strong or weak (in effect, strong increases the reference count, while weak does not). If a reference count becomes 0, it is removed from memory. So if everything was weak, properties would be removed from memory immediately, and you could not use them. You need a strong somewhere, in effect.
As a simple way to plan how to structure your strong / weak definitions, think of one particular class as the owner of a property, and give that one the strong. This means the property will stick around for that owner to use it. If another class also has a reference to that property, but does not require that the property always sticks around for it - make that weak. As long as the main owner class, say the View Controller, still exists - then so will the property. If both were set weak, then even though the property still appears, it will be empty, because at runtime, it was decided that no-one really needed it, and it was removed.
This is because you will need one strong reference as long as you need a reference to the object, as long as you need the referred object to survive in memory.
(You could go back to Objective-C where you can do all the works without ARC and do the memory management manually on your own. Apparently this could make you appreciate ARC.)
On the other hand you could ask the same question for strong references. The difference is that when you keep strong references only (no weak ones) then you don't have to think about weak or strong and it would work. (weak alone will most likely not work)
But then you would have to be 100% sure to null every strong reference to each object that you don't need anymore.
Side note:
When you add your object to a collection such as an array or a set (or as subview to a view) then these collection objects will keep strong references for you. In that case you don't have to care but to remove the object from that collection when the object can be discarded. This "trick" is only appropriate when you maintain these collections anyway - e.g. when it is about views that are part of the view hierarchy as long as they are required.
Under ARC object instance is alive as long as there is at least one strong reference to that object instance.
If you mark all variables as weak, there would be no strong references to object instance and nothing could keep it alive. It would be destroyed immediately upon creation.
If you are taking more than single reference to object instance you have to think about whether it should be marked as weak or strong (depending on particular code), there is no way around it.
I am debugging the my Cocoa application and noticed using the Instruments.app allocation profile that the model object graph is not deallocating as I expect. Basically when I remove a root model object from the my NSDocument I was expecting the whole object graph for that object to be deallocated. That doesn't happen, which means that there is a strong reference to my root model object somewhere else in the application.
Is it possible to get a list of object which have a reference to a specific object in Cocoa either programmatically or using Instruments.app? If I could know where the strong reference is held this would help with debugging this problem.
I found this similar question, How to get the reference count of an NSObject?, but this simply says which how many references there are, not which objects hold the references.
Not in this way. The only information that is stored inside of a running process is the number of retains on an object (number of times "retain" has been called net the number of times "release" has been called). This is not the same as the number of references (number of "strong" pointers to the memory). Most memory locations do not have a side table of things that point to them in Cocoa.
In Instruments, you can turn on "record reference counts" and see everywhere the retain count is increased or decreased. See Instruments Allocations track alloc and dealloc of objects of user defined classes for a good explanation of how to do that. This won't tell you where you've made your mistake, but it will tell you where the retains are occurring.
This is a purely theoretical question so please feel free to propose additional constraints or suggest alternative questions to discussion.
I know that in Objective-C almost any property that is mutable ought to be defined as (nonatomic, copy), but what if I want that property, say a mutable array, to be only just a list of weak pointers to objects already retained by some other class? For example, say I have a class called Concept and let's assume that all intended Concept instances have already been loaded and retained in memory somewhere by some class other than Concept. Say we add a nonatomic NSMutableArray property to each Concept instance called relatedConcepts that points to other Concept instances that are retained and already exist in memory.
What I know is that NSArrays are lists of strong referenced objects, but I want a list of weak referenced objects. How might I go about implementing this?
An NSArray ALWAYS keeps a strong reference to each of its items. This is by design as arrays are nil terminated and would become corrupted if one of the items ever became nil. The weak you are referring to would only apply to the array reference itself. Keeping "weak" items in an array is an advanced problem. My current solution involves using weak proxy objects which sounds kind of like what you were describing in your last paragraph. Whether you use copy or not isn't really relevant.
I have found some scenarios where keeping an array of weak references has come in handy. I've had to roll my own solution for that using weak proxies. Basically, you have an array of objects that then have a weak reference to the objects you actually care about. The weak proxy then needs to redirect all of the calls to it to the object it wraps.
You would use the strong specifier for your relatedConcepts property. That's to keep that instance from disappearing out from underneath you, regardless of the number or kind of references it contains.
There's no such thing as a mutable array that's a "list of weak pointers." NSMutableArray (as well as NSArray) maintains strong references to its members.
You're worrying far too much about what's retained (has a strong reference to it) and what isn't. That isn't your job. It's entirely possible that whatever is holding a strong reference to an object may choose to release it. Meanwhile, if nothing else is holding a strong reference to it, your weak reference falls to nil. Bad idea if you want to keep using it.
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