Objective-C: detect if an object has no more holders - iphone

I use an NSMutableArray as a "Window Manager" to store all my NSView objects, I send it like
[windowManager addObject:self];
right after ViewDidLoad of each NSView;
the question is, how can I detect on my "windowManager" NSMutableArray if it stores objects that has been called to release (and windowManager is the only one holding their instance)?
After I call a release upon an one of these NSViews and turn
SomeNSViewPointer = nil;
, I also need the WindowManager to check the above.

When you do this:
[someNSViewPointer release];
SomeNSViewPointer = nil;
Do this instead:
[someNSViewPointer release];
[windowManager removeObject: someNSViewPointer];
SomeNSViewPointer = nil;
I would have to question why you are storing two references to your view though. I think your design needs reviewing.

You can send the view you are checking the -(int)retainCount message and check if it is one (ie the single retain from adding it to the array)? This question raises a red flag that you are doing soemthing that might be considered bad design, why are you doing this?

When you add anything to your NSMutableArray via 'addObject', that NSMutableArray does a retain on it. You will need to remove that object from the NSMutableArray to have it released, if that is what you are after.
[yourNSMutableArray removeObjectIdenticalTo:yourNSView];

Maybe try if([myView retainCount] == 1), then it is only retained in the windowManager.
[EDIT] Read about retainCount in NSObject reference. As JeremyP suggests, it is not smart to use this in your code, but think better of your code design.
http://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Protocols/NSObject_Protocol/Reference/NSObject.html#//apple_ref/occ/intfm/NSObject/retainCount

Related

Memory retain and release

I wanted to know that if I am doing correctly, regarding retain and release of digitbutton reference down below. In the book "Programming in Objective C by Stephen Kochan", it says in one section with different example that like digitbutton is holding reference to same object that sender is holding, so in between sender may get released elsewhere and then I would be calling currenttitle method on deallocated digitbutton, if that is the case.
So here I called retain on digitbutton and after its use I called release on it. Well, the example maybe not genuine for this purpose but is this concept valid? Should I make practice to retain objects like in example below?
I am not experienced in writing managed codes.
- (IBAction)clickDigit:(id)sender {
UIButton *digitButton = (UIButton*)sender;
[digitButton retain];
NSLog(#"%#",[digitButton currentTitle]);
[[self displayOutput] setText:[digitButton currentTitle]];
[digitButton release];
}
Update
Yes, the example was wrong; let's say if instead of id sender there is NSString and instead of UIButton, same NSString, and return type is just void. Then do I have to retain sender NSString for safety in case someone holding its reference releases it.
Note: I am not using ARC.
You dont need to retain & release it.
You can do like this
- (IBAction)clickDigit:(UIButton *)sender
{
NSLog(#"%#",[sender currentTitle]);
[[self displayOutput] setText:[sender currentTitle]];
}
What you're doing in clickDigit: is unnecessary.
Instead of me explaining why it is unnecessary, I will point you directly to where I learned proper memory management techniques: Advanced Memory Management Programming Guide

Ignore 'Object sent -autorelease too many times" warning

When running Analyzer, I receive the "Object sent -autorelease too many times" warning. I know I'm doing something tricky (and am open to alternatives to accomplishing my goal).
Essentially, I wanted to keep a stack of specific types of controllers handy for sending messages from a central location. So, considering:
From 'ActionBroker' shared object:
NSMutableSet *liveActions;
liveActions = [NSMutableSet alloc] init];
...
CLViewController *action = [[[actionClass alloc] init] autorelease];
if (!action) {
return nil;
}
[self.liveActions addObject: action];
// When adding an object, the retain count is increased. We want the action
// to dealloc, so that it might remove itself from the list...
[action release];
return action;
And the complementary dealloc code:
[[CLActionBroker sharedActionBroker] removeAction: self];
[super dealloc];
... and in removeAction:
[action retain];
[self.liveActions removeObject:action];
The above code works, I just get the nagging error. And the nagging sensation that I could probably solve the problem a different way.
One of the use cases for this code is to pass an 'handleOpenURL' request through a chain of open controllers and returning the first 'YES' response.
As I understand it, your intent is to have a set of controllers ("actions") in the set, which you could easily access from anywhere in your app. If one of these controllers is deallocated, it would automatically remove itself from the set.
There's a problem with this setup, though. As you've described it above, the controller is supposed to be removed from the liveActions set upon deallocation. But since an NSSet retains its members, your controller will never be deallocated so long as it is still in the liveActions set. -dealloc is only run when all retains have been balanced by a release.
This, then leads to your over-release, which leads to the warning. Since you've sent an extra release, -dealloc could be run while the controller is still in the liveActions set. But when you remove it from that set, it's going to send a release message to your object, which would take its retain count negative. That may or may not be safe, but in either case it's an ugly workaround.
What you really want, it seems, is a set that does not retain its members. This is normally a dangerous configuration, since it can lead to dangling pointers in the set. But if you're willing to manage the lifetime of the object and clear out those dangling pointers at the appropriate times, it turns out that it's quite doable. You just need to use a CFMutableSet instead of an NSMutableSet. And since the two are toll-free bridged, it doesn't even add that much complexity.
Setting up the CFMutableSet looks like this:
// The NULL for the third parameter tells the set to not do anything when
// an object is added to or removed from the set.
CFMutableSetRef cfLiveActions = CFSetCreateMutable(NULL, 0, NULL);
// Toll-free bridging makes this possible
NSMutableSet *liveActions = (NSMutableSet *)cfLiveActions;
After that, you can use it exactly as you would use any other NSMutableSet; this one will just be special in that it won't retain its members.
The problem:
CLViewController *action = [[[actionClass alloc] init] autorelease]; // +0 ownership
// ...
[self.liveActions addObject: action]; // liveActions takes ownership
// ...
[action release]; // -1 ownership
When you've alloced the object, you are responsible for it. But then when you autorelease it, you've fulfilled your obligation (and because you autoreleased instead of releasing, you can still use the object until the next turn of the runloop). You shouldn't release it again later.
Also:
[action retain];
[self.liveActions removeObject:action];
The retain is unnecessary.
(And here's another plug for beginning to switch to ARC, under which you won't have to worry about this!)
[Edit: misunderstood your question. Revised answer forthcoming.]
[Edit 2: never mind... even though I misread your intent, I believe my solution is still right.]

I have to retain a NSMutableArray although it's a property

I'm new, but I read a lot about memory management, and really tried to find the answer myself.
It sounds so basic and yet I apparently don't get it.
I have a NSMutableArray as property in .h:
#property (nonatomic, retain) NSMutableArray *documentListArray;
I synthesize it and release it in (void) dealloc.
To populate the array, I have a method - (void)updateRecordList and in there:
self.documentListArray=[DocumentDatabase getDocumentListSortedByDate];
EDIT:next line:
[[self recordListTableView] reloadData];
where DocumentDatabase is a different class with the class methods getDocumentListSortedByDate and getDocumentList.
Here is what is happening in getDocumentListSortedByDate:
NSMutableArray *returnArray = [DocumentDatabase getDocumentList];
//sorting...
NSLog("array count:%i",[returnArray count]); //returns correct numbers first and second time
return returnArray;
and in getDocumentList
NSMutableArray *returnArray = [NSMutableArray arrayWithCapacity:files.count];
//add file data to array...
return returnArray;
This works the first time I call updateRecordList, but after adding a file and calling updateRecordList a second time, it crashes with (using NSZombies):
* -[NSCFNumber dealloc]: message sent to deallocated instance 0x7504c90.
With a lot of logging I narrowed the problem down to the line above in updateRecordList and it works if I change it to:
self.documentListArray=[[DocumentDatabase getDocumentListSortedByDate] retain];
My conclusion is that the array down in getDocumentList has been autoreleased before it arrives. So my questions are:
1. Why do I have to retain it there? Shouldn't that happen by itself by declaring the property (retain)?
Or, in other words, why is the array autoreleased too early (assuming this is what is happening)?
2. When I assign a new array to self.documentListArray, is the old array automatically released? If I try to release it myself before getting a new documentList, it crashes too.
Thanks in advance for any reply.
EDIT:
Maybe I'm an idiot: I failed to mention that documentListArray is the data source for an UITableView (see the added line on top). I suspect that I am doing something wrong with populating the table view, and the array gets retained...? It does however crash on assigning the property, not on reloadData.
I go back to study if I use the UITableViewDataSource protocol properly. Thanks to everybody, your answers brought me hopefully on the right track. Will update when solved.
EDIT2:
It works now without retaining, and I think I understand why: I debugged extensively and found that Objects contained in Objects added to the array where nil. Particularly, deep down in encodeWithCoder I did not use "self" when assigning values. When decoding, those values where nil. Since I changed that, it seems to work.
I suspect that not assigning the new array caused the crash, but the TableView which would read the new array-even before I call reloadData. Which would lead back to Davids question of synchroneous access. Thank you all for your help.
The code you've shown it appears correct; it should not be necessary (or correct) to call retain yourself, as long as you are assigning the value to a property with retain semantics before the autorelease pool is drained. Are all the calls (getDocumentListSortedByDate, getDocumentList) happening synchronously, or are you doing any of this in the background? Double-check that you're assigning using the "self." ("self.documentListArray =") instead of just assigning directly to the instance var ("documentListArray ="); if you omit the "self.", the setter is bypassed.
No, don't free the old value before assigning; that's the setter's job.

iphone - not leaking

What's the right way to do this?
I have an array that I will use on several methods. I will add objects to it, get values, replace values, etc.
Today I do this:
I declare it on .h, using something like
NSMutableArray *myArray;
as soon as the application starts, I declare it on .m doing something like
myArray = [[[NSArray alloc] init] retain];
If I don't add the retain the array will be released at some point and the application will crash. But allocating the array at the beginning of the application and left it "open" without releasing it will make instruments cry, pointing the finger at me, calling me a "leaker"...
How to solve that? Is this the correct way to do that? how do you guys do stuff like this?
thanks
alloc implicitly sets the retain count to 1. By sending the retain message you're incrementing the retain count to 2. In order for the object to be deallocated you would then need to release it twice. Failure to do so would result in a memory leak.
Ideally you should create the object in your init method using [[NSArray alloc] init] and then release it in your dealloc method like so:
- (void)dealloc {
[myArray release];
[super dealloc];
}
You might also find this article useful: http://developer.apple.com/mac/library/documentation/cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
One more thing: You declared myArray as an NSMutableArray but instantiated it as an NSArray. Perhaps that's causing the crash.
You should not retain the object you've just created. You already own it. If, as you say, "the array will be released at some point and the application will crash," that is the code you should change. Your code shouldn't be releasing an object that you still want to keep around.

Where I should call [object release]?

I've subclassed some UITextField and added some custom properties.
In a UITableViewController, in the ViewDiDLoad I init them, and in the cellForRowAtIndexPath I add them to the cell with [cell.contentView addSubview:customTextField];
Each cell has a different customTextField as all of them are very different.
Where I should call the [customTextField release] ?
After I add them to the cell view ?
If for example I call [self.tableView reloadData] my customTextField are going to be added again to the cell, so maybe I should change my approach in doing this ?
thanks for the orientation ...
regards,
r.
You release an object when you no longer have any interest in it. This happens for many reasons; it might be because you've finished with the object, or have passed the control over the object lifetime to another object. It might be because you're about to replace the object with a fresh instance, it might be because you (the owner object) are about to die.
The last one seems to be relevant in your case. You create these object in viewDidLoad and repeatedly need them (i.e. to add them to cells) until your object is no longer functioning. In this case, just as you create them in viewDidLoad, you can release them in viewDidUnload.
Edit: I should really mention autorelease, but this isn't relevant in this instance. Memory management is best handled with a notion of 'owner' - the person who creates something (or retains it) should be responsible for deleting it (or releaseing in ObjC parlance). autorelease handle some cases where you need to give an object to an alternate owner, having previously owned it yourself (typically via a method return). If you are the creator, you can't just release it before returning it to the new owner, as it will be deleted before the new owner has a chance to stake an interest in it. However, you can't just not release it; it will leak. As such, the system provides a big list of objects that it will release on your behalf at some point in the future. You pass your release responsibility to this list, then return the object to the new owner. This list (the autorelease pool) ensures your release will occur at some point, but gives the new owner a chance to claim the object as theirs before it's released.
In your case, you have a clear interest in owning the objects for the lifetime of your view controller - you need to, at any time, be able to add them to view cells in response to a table data reload. You're only done with them when your view controller dies, so the viewDidUnload (or possibly dealloc) is the only sensible place to release them.
I always release my controls directly after I added them to a view using addSubView. When I work with tables, I also initialize them in the cellForRowAtIndexPath method.
Therefor the object stays alive the shortest time.
Adam Wright explains the theory of this very well, but let me give you some practice. You're thinking about this problem far too hard, and that almost always leads to bugs. There is a simple solution that solves this problem almost every time: Retain all ivars using accessors; don't retain non-ivars.
.h
#interface ... {
UITextField *_customTextField;
}
.m
#property (nonatomic, readwrite, retain) UITextField *customTextField;
...
#synthesize customTextField=_customTextField;
-(void)viewDiDLoad {
self.customTextField = [[[UITextField alloc] init....] autorelease];
}
...
- (void)dealloc {
// I do not recommend accessors in dealloc; but everywhere else I do
[_customTextField release]; _customTextField = nil;
}
Never access your ivars directly, except in dealloc (even that is controversial and some people recommend self.customTextField = nil; in dealloc; there are arguments either way). But never assign your ivars directly. If you will follow this rule, you will find that most of your memory problems go away.
The safest way to handle object ownership is to autorelease the view directly after initialization:
FooTextField* textField = [[[FooTextField alloc] init] autorelease];
[myCell.contentView addSubview:textField];
Adding the text field to a superview (the UITableViewCell's contentView) retains it. This way you don't have to care about releasing the view afterwards.
There seems to be a resentment against autorelease in the iPhone developer community. In my opinion, this resentment is unfounded. Autoreleasing an object adds very little overhead to the program if the objects lives longer than the current pass through the run loop.
Every object in Obj-C has a reference counter (retainCount), and when this counter goes to 0 the object is deallocated. When you allocate and initialize an object, the reference count is set to 1 - but you can retain it as many times you want to.
UITextField *textField = [[UITextField alloc] init]; // Reference counter = 1
[textField retain]; // Reference counter = 2
[textField retain]; // Reference counter = 3
The opposite of retain is release, which subtracts from the reference counter;
...
[textField release]; // Reference counter = 2
[textField release]; // Reference counter = 1
You can always get the reference counter of your objects;
printf("Retain count: %i", [textField retainCount]);
The method addSubview of UIView does retain your passed in sub view - and when it's done with it it releases it. If you need your UITextField later, in another scope (when the UIView is done with it and has released it) - you should not release it after you've added it to the super view. Most of the time you actually don't need to hold on to a reference, so you should release it after you've added it to the super view. If you dont - you can release it in the dealloc method of your scope.
Take a look at UITableView -dequeueReusableCellWithIdentifier: and -initWithStyle:reuseIdentifier:.
In -tableView:cellForRowAtIndexPath:, use -dequeueReusableCellWithIdentifier: and check if the result is nil. If it is, instantiate a new cell with -initWithStyle:reuseIdentifier:.
Send -autorelease to your customTextField upon creation and adding to the respective cell.
You should not add subview in cellForRowAtIndexPath! This will slow down the view as you add a subview each time the cell is displayed. Try using custom UITableViewCell class for that purpose.
Here is a perfect solution of UITableView customization
http://cocoawithlove.com/2009/04/easy-custom-uitableview-drawing.html
works jut perfectly