Why does view property of UIViewController increments retainCount on every access? - iphone

So this is the question. Why does it do it? Even when I do something like this
NSLog(#"view's retainCount %d", [viewController.view retainCount]);
it increments the retain count.

Don't look at the retain count.
The viewController's getter is holding it for you so it can return a reference for you to (in this case) access the "retainCount" variable. It is returning this reference as "autorelease", so it is not really perminantley holding the retain count.
For example, if you ran this NSlog function 5 times in a row, you might see the retainCount go up by 5, but if you created a UI button that called it - thus giving the autorelease the ability to kick-in between presses of the button - you would not see it going up forever.

Related

iPhone: How Memory works when adding to subview

A general question: When you add an item to an UIView, does that increase the owner count by 1? Does the main view that you added the item to now becomes an owner as well?
Example:
mainView = [[UIView alloc] init];
UILabel *label = [[UILabel alloc] init];
[mainView addSubview:label] //does this increase owner count by 1?
[label release] //and this decreases it by 1?
You release what you retain/init.
When you call addSubview:, it increases the retain count (or as you say owner count). But that increase belongs to mainView. So it is up to mainView to release the subview some point in the future, not you.
So when you init the label it increases the retain count to 1. When you call addSubview:label it increases the retain count by 1, to 2. Then you release the label, decreasing the retain count back to 1 and counteracting you're previous init.
Then when the label is removed from the mainView its retain count will go back down to 0 and it will be deallocated.
Never use the method retainCount, whether you're just observing it, or acting on it. This method will not display what you expect because of a lot of behind the scenes code. Just don't use retainCount.
Subviews are stored in a NSArray which sends a retain to every object added. In principle, yes the retain count goes as you expect but in reality you can never observe the retain count reliably because of all the retains and releases that occur behind the scenes in the API itself. Trying to track the retain count directly will just lead to grief.
It's better to just follow the rule of if you create an object with new or alloc-init then you release it. If you don't do the former, you don't do the later.

Deallocating and removing UiButtons

I am trying to make a program that dynamically creates a button using the command:
[UIButton buttonWithType:UIButtonTypeRoundedRect]
But when I use these commands the delete the button I create:
[currentButton removeFromSuperview];
[currentButton dealloc];
[currentButton release];
I receive an error. How would I go about removing and deallocating the memory from a uibutton?
I got this problem long time ago, please notice that
[UIButton buttonWithType:UIButtonTypeRoundedRect]
has autorelease inside, so in your initialisation you need to do retain, like this:
_mybutton = [[UIButton buttonWithType:UIButtonTypeRoundedRect] retain];
and later point, you can do:
[_mybutton release];
Hope helps
:)
In the Objective-C/Cocoa framework, you encounter two different ways to receive objects: ones which you have explicitly allocated memory for (via a constructor) and ones that you have received memory reference to (via a class method).
FooBar *fone = [[FooBar alloc] initWithText:#"Hello, World!"];
In this example, memory is explicitly being allocated for the object by your call, using the alloc method, and then it is being initialized with data using the initWithText method that would have a method header like this:
- (id)initWithText:(NSString *)text;
On the other hand, you also will encounter objects that are created by classes automatically for you. An example of this would be below:
FooBar *ftwo = [FooBar fooBarWithWelcomeText];
In this example, a FooBar object is being returned even though we are not calling alloc to allocate memory for it. There are many different reasons to implement the method like this, but its mainly done to abstract certain details from the code that is using the object. The above example would have a corresponding method header like this:
+ (FooBar *)fooBarWithWelcomeText;
Depending on which approach is used, it changes how you interact with the memory of the object. So for the first example, after allocating the memory for the object you receive it back with a retain count of 1. If you are done using the object, you need to explictly release it with
[fone release];
In the second example, you are receiving an autoreleased object, which will be deallocated once the autoreleasepool is drained. If you want to keep it, you must explicitly retain with:
[ftwo retain];
If you do not wish to retain it, you can just leave it as is and it will be deallocated automatically. You can tell a method uses autorelease by two characteristics: 1) you will not utilize alloc when you receive the object; and 2) there will be a "+" next to the method heading. This means that the method is declared as a class method (similar to Java static methods).
So to finally answer your specific situation, you only need to make sure that the retain count is lowered to 1 (The only object having a reference to it was the autorelease pool). In your example, it would be done like this:
UIButton *currentButton = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[someView addSubView:currentButton];
// some code later
[currentButton removeFromSuperview];
You do not need the release statements because you never explicitly retained it. When you added the UIButton to another view, it retained the object so it incremented the reference count to 2. By removing it from the view, it is lowered back down to 1 so that when the autorelease pool is flushed, your UIButton will be deallocated. Btw, never call the dealloc method directly. When the retain count of an object is decremented to 0, the dealloc method will automatically be called.
You can't not dealloced the UIButton which you get through buttonWithType from system,if you don't alloced any instance then you are not entitled to call release on that.
In your case, you can use removeFromSuperview but not either dealloc or release .
You can't call dealloc directly on object, this is invoked by the system when you say release on object.
EDITED:
you could create you button using initWithFrame function of UIView. But you will get only the button type UIButtonTypeCustom which is by default, and also can't not change the button type because it's readonly property. So you would get the rounded button by using your some images.
You're not supposed to call dealloc directly. Try removing the dealloc line and see how that works.

Memory management question

I wanted to clarify something:
I.e when I add a UIVIEW To i.e another UIVIEW programatically.
i.e [theview addsubview:thechildview];
I need to release thechildview because theview increments the retain count.
Right?
So basically if I have a loop and I am using that loop to initialise subviews and add the subviews to a view:
for(int i=0;i<10;i++){
UIView *child = [UIview alloc]init.....
[parent addSubview:child];
[child release];
}
I still need to release the child right ? So that would mean the child view still has a retain count of 1 right ?
The thing is whenever I call the release on the child after adding , the child gets deallocated after a while.
Can you guys confirm am doing the right thing by releasing the child after it is added ?
thanks.
You should release it somewhere, correct. You can hold on it and release it later, but it is your responsibility to do it somewhen.
The parent view will retain/release its childs on its own. So if the parent view gets dealloc'ed, it will release its child views.
If a child view is removed from its superview, it will also be released.
I need to release thechildview because theview increments the retain count.
Right
Wrong.
You need to release theChildView if you own it and if you have finished using it. Whether theview chooses to retain it or not is completely irrelevant.
So if we look at your loop
for(int i=0;i<10;i++){
UIView *child = [UIview alloc]init.....
[parent addSubview:child];
[child release];
}
the code is correct because at the beginning of the loop block you alloc the child which means you own it and at the end of the loop it is about to go out of scope which means you no longer need it so you must release (or autorelease) it.
What happens in between is totally irrelevant and you should not assume anything about what any particular method on another object does (except that it adheres to the Memory Management Rules).
I still need to release the child right ? So that would mean the child view still has a retain count of 1 right ?
You cannot assume anything about the retain count. parent might cause the object to be retained more than once if it hands it off to other objects. It might choose to copy it instead of retaining it. If it doesn't need to keep a reference it might not retain it at all.
Can you guys confirm am doing the right thing by releasing the child after it is added ?
No, you are doing the right thing by releasing it when you have finished using it.

EXC_BAD_ACCESS after calling popViewControllerAnimated

I'm getting an EXC_BAD_ACCESS after calling dismissModalViewControllerAnimated on my view controller. My project is based on the table view starter project, and RootViewController creates a view like this:
GobanVC *vc = [[GobanVC alloc] initWithNibName:#"GobanVC" bundle:[NSBundle mainBundle] coll:c];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
In GobanVC.m, I'm handling a button to dismiss the view:
- (IBAction) onDone:(id) sender;
{
[self.navigationController popViewControllerAnimated:YES];
}
For some reason the GobanVC object is getting over-released. I ran the allocation instrument, and I can see the reference count get set to 1 when I call alloc, then UIKit calls retain/release a bunch of times, and then my release above is handled. After that, none of the retain or releases are from my code, and after popViewControllerAnimated, the count becomes -1 eventually.
If I take the release above out, things seem to work okay, so it seems the count is off by exactly one somewhere.
Any ideas?
Maybe there is something wrong with GobanVC. Do you have the implementation for it?
Because if there is a retain for every release made by UIKit (there should be).
And you say that your calls are balanced as well (one alloc and one release).
Then it means that in the implementation of GobanVC there must be something wrong.
Let's count the retains:
Alloc makes it 1.
Release makes it 0.
So, what comes out of the stack when you call pop will have retain counter equal to 0, which is NOT what you want. If you either remove release or keep it and assign "vc" to an instance variable defined as "retain" property, you will be fine.
From the code you provided I can see the following:
Root controller creates an instance of GobanVC. The GobanVC's retain count is 1.
Root controllers pushes GobanVC instance to the navigation stack. I am not sure if push increases the retain count. Most probably yes. Then the retain count of GobanVC instance becomes 2.
You release the GobanVC instance, setting its counter to 1.
Your button handler resides in GobanVC (not in the Root controller). So, GobanVC pops ITSELF from the stack with retain count 0 (because if push increases the counter, pop will decrease it). This IS a problem.

NSMutableArray removing the object but then app crashes

I have what I think is a weird error but of course i'm relatively new to iPhone Development so it's possible that it's not all that weird after all.
I have an array (NSMutableArray) of objects that I am keeping track of (one is added to the array every time the user touches a button) what I'm trying to do is when the array reaches a certain value I add the new object to the beginning of the array and then remove the last object in the array. When I step through my code everything works the object is removed but then the app just crashes...the debugger isn't on any line of code when the app crashes and there are no loops or timers in the app yet so I can't think of anything else that is running.
here is the code that is executed right before the crash
if([objectArray count] > 10)
{
MyObject *objectToRemove = [[MyObject alloc] init];
objectToRemove = [objectArray objectAtIndex:10];
[objectArray removeObjectAtIndex:10];
[objectToRemove removeFromSuperview];
}
the main point of this code is that everytime the user touches a button an object is added to the screen and displayed, and then when the number of objects reaches 10 and the user touches the button again the first object that was added is removed and the new object is displayed. If I comment out the removeObjectAtIndex line everything works as intended but the array continues to grow.
I've also tried removing the object after the UIview is removed and the app behaves the same way. If I try to remove an object from the array at a different index (I.E 3) the app doesn't crash but it doesn't give me my expected result. but like I said the code runs fine and when I check the count of the array before and after the execution of the line the value is 11 and 10 respectively.
Any help you can provide would be appreciated,
BWC
I don't think this does what you think it does. Going line by line:
MyObject *objectToRemove = [[MyObject alloc] init];
You allocate a new object of type "MyObject.
objectToRemove = [objectArray objectAtIndex:10];
You overwrite your local MyObject pointer with whatever was at index 10 in objectArray. The objectToRemove that you initially allocated is now leaked.
[objectArray removeObjectAtIndex:10];
Now you've removed the object at index 10. When you remove it from the array, it is released and its reference count is decremented. This may (or may not) cause it to be deallocated.
[objectToRemove removeFromSuperview];
Now you are sending a message to the object that was previously in the objectArray. If the object was deallocated by the previous line of code, I would expect a crash.
Putting aside the leak in the declaration, you might be able to do this:
objectToRemove = [[objectArray objectAtIndex:10] retain];
[objectArray removeObjectAtIndex:10];
[objectToRemove removeFromSuperview];
[objectToRemove release];
This would prevent the object from being deallocated out from under you, if that's why the crash is happening.
One way to determine if my scenario is what is happening is to turn on NSZombies, which you can do by setting the environment variable NSZombieEnabled to YES.
How about:
MyObject *objectToRemove = [objectArray objectAtIndex:10];
[objectToRemove removeFromSuperview];
[objectArray removeObjectAtIndex:10];
removeFromSuperview will decrement the retain count once on objectToRemove. (removeFromSuperview implies that it was added to the view previously and that had retained it and incremented its retain count by one). removeObjectAtIndex will decrement it again. But in this order, you most likely will not be releasing it when you still need it around. (Of course, adding to the array also retained it so the order may not be that important).