Does a subview viewcontroller added via addSubiew need a dealloc? - iphone

normally when I'm using a viewcontroller that will push the current viewcontroller out of the way, I use a UINavigationController and push/pop the new viewcontrollers and let them handle all the dealloc themselves.
However, for example, in this case, I have a MainViewController, which is the default view when the app starts up. I have a second view, called SecondaryViewController, that is a popup on the main screen (sort of like a lightbox).
Here is the code to illustrate:
//From within mainViewController:
secondaryViewController = [SecondaryViewController alloc] initWithNibName:#"SecondaryViewController" bundle:nil];
[self.view addSubview:secondaryViewController.view];
The secondaryViewController interface looks like this:
//interface
#interface SecondaryViewController : UIViewController
{
IBOutlet UILabel *httpLabel;
IBOutlet UIScrollView *scrollView;
}
#property(retain, nonatomic) IBOutlet UILabel *httpLabel;
#property(retain, nonatomic) IBOutlet UIScrollView *scrollView;
As for the implementation, I have the #synthesize for the #property ivars, but I'm not doing any manual allocs. However, I did put a dealloc method:
- (void)dealloc
{
[httpLabel release];
[scrollView release];
[super dealloc];
}
But I'm not sure I need the above.
So my questions would be the following:
1) Do I need the above dealloc method in this case? Or more generally, when would a subview need a dealloc method?
2) If I do or dont need it, does it depend on whether I'm adding the secondaryViewController via addSubview or pushViewController? For instance, if I wanted to replace the entire mainViewController, with this:
[self.navigationController pushViewController:secondaryViewController animated:NO]
Would the secondaryViewController need a dealloc method?
Thank you!

Yes, you do need the dealloc method exactly as you have it, in this case. You are on the right track because you're assuming that since you are not doing any manual allocating, you don't need to do any dealloc/releasing... however, by specifying the property as (retain, nonatomic), you are doing implicit retaining.
This means that if you ever set those properties, what's actually occurring under the covers is something like this:
-(void)setHttpLabel:(UILabel *)newlabel
{
if (newLabel != httpLabel)
{
[httpLabel release];
httpLabel = [newLabel retain];
}
}
As you can see, your synthesize is causing a retain to occur on an object. If you never balance that retain out with a release, it will leak. So the only logical place to put it, is in your dealloc method. This creates the circle of life.
If you never set these properties and don't have release in dealloc, then it won't leak anything, but you obviously wouldn't want to make those assumptions.
If you didn't have any retain properties or any manual allocing of ivars, then and only then, can you nuke the dealloc method.
Hope that helps.

I think this is allowed in the latest iOS 5+ but previously you were not supposed to add another viewcontrollers view to your main viewcontroller. This is clear misuse and can lead to issues.
The concept of viewcontroller is one who controls all the views. A view controller should not control another viewcontroller unless it is a container viewcontroller such as UINavigationController/UITabBarController.
So please rethink the design. why do you need the SecondaryViewController. Why cannot the mainviewcontroller manage the secondary view as well?
Lastly, every viewcontroller should have the dealloc in it.

If you need to access secondaryViewController from your main view controller after you've added its view to the hierarchy, you should not deallocate it at that point. If you don't need to access the secondary controller after you've displayed it, you can dealloc it at that point.
In practical terms, if secondaryViewController is an ivar, it probably makes sense to keep a retained reference to it. If it's a local variable and you're not accessing it later, you should dealloc it.

Related

Why a separate instance of ViewController Class is affecting previous instance?

UPDATE - Cause found!... please read below & suggest the solution:
While creating a video to show this issue, I have found why does that happen...
Any Control/Element that is defined between #imports & #implementation DetailViewController in .m file is lost by the original detVC when a new instance is created of VC.
Example:
I am including a video that re-creates this issue. As shown, all controls work except a UISegmentedControl & a UILabel.. both of which are defined in DetailViewController.m as:
#import "DetailViewController.h"
UISegmentedControl *sortcontrol;
UILabel *incrementLabel;
#implementation DetailViewController
Video Link - http://www.youtube.com/watch?v=2ABdK0LkGiA
I think we are pretty close.. Waiting for the answer!!
P.S.: I think this info is enough can lead us to the solution, but if needed I can share the code too.
EARLIER:
There's a detailViewController (detVC) in our app which is normally 'pushed' from a UITableViewController.
Now we are also implementing Push Notifications which would 'modally' present the same detVC whenever the notification arrives, which in turn can happen over any view controller.
It works all fine until the detVC through notification is presented while another instance of detVC was already the active view controller (pushed earlier through TableView).
The problem happens when the presented detVC is dismissed - the pushed detVC 'looses' control of about everything - It is not pointing to the same data as earlier & even the UISegmentedControl on Navigation Toolbar points to -1 index on pressing any index!
Why does such thing happen when we are creating a separate instance of detVC? Why does it affect the earlier detVC? How can it be resolved / What about Objective C needs to be learned here? (FYI, we are using ARC in the project)
Thanks
EDIT - Some code:
When Pushed:
ProductDetailViewController *detailViewController = [[ProductDetailViewController alloc] init];
detailViewController.hidesBottomBarWhenPushed = YES;
[self.navigationController pushViewController:detailViewController animated:YES];
detailViewController.serverOffset=serverOffset;
detailViewController.prdctIndex = indexPath.row;
When presented through Notification:
- (void)displaySingleProduct:(NSDictionary*)userInfo{
ProductDetailViewController *onePrdVC = [[ProductDetailViewController alloc] init];
UINavigationController *addNav = [[UINavigationController alloc] initWithRootViewController:onePrdVC];
onePrdVC.justOne=YES;
onePrdVC.onePrdIDtoLoad=[[[userInfo valueForKey:#"prd"] valueForKey:#"id"] intValue];
[self.navigationController presentModalViewController:addNav animated:YES];
}
There is a Product class in detVC which gets the data from the values stored at the prdctIndex row in the SQLite table.
After dismissing the presented detVC through notification, the Product class of the pushed detVC starts pointing to the row which was used for Product class in presented detVC. And there is no such code in viewDidAppear which would alter Product class.
So, this, UISegmentedControl issue mentioned above & some other problems are causing the pain! Basically, the whole detVC acts weird - which re-works if we just pop & re-push it!
EDIT 2
I have more info on it which could lead to the cause.
If I run viewDidLoad on viewDidAppear like this:
if (!justOne) {
aProduct = [allProducts getProduct:(prdctIndex+1) one:NO];
[self viewDidLoad];
[self populateDetails];
}
the pushed detVC regains the control & every element/control starts re-working as expected (but not in the ideal way). So, it is quite clear that the separate instance of presented detVC does messes up the earlier pushed detVC & a re-setting up of all the controls / pointers is required through viewDidLoad.
Can any helpful conclusion can be derived from this info?
When in your code you write:
#import "DetailViewController.h"
UISegmentedControl *sortcontrol;
UILabel *incrementLabel;
#implementation DetailViewController
You are not defining these variables as instance variables (ivars), so they are not individual for each instance. There are three ways to define ivars.
1) The traditional way, in your .h file.
#interface DetailViewController{
UISegmentedControl *sortcontrol;
UILabel *incrementLabel;
}
2) Some additions to objective-C have added support for the next two ways. Like declaring them in your .m file.
#implementation DetailViewController{
UISegmentedControl *sortcontrol;
UILabel *incrementLabel;
}
3) If these ivars use properties to expose them, then you can simply leave out the explicit definition of them. So in .h:
#interface DetailViewController
#property (strong, nonatomic) IBOutlet UISegmentedControl *sortcontrol;
#property (strong, nonatomic) IBOutlet UILabel *incrementLabel;
and then in the .m file:
#implementation DetailViewController
#synthesize sortcontrol;
#synthesize incrementLabel;
Are you loading the view controller from a NIB? If so, it may be that the same instance of the view controller is used each time. You can log the address of your view controller in viewDidLoad and see if this is the case.
It actually depends on how your apps architecture has been created. If you are using TabBarController based application, here is the proper way to do so.
If two instances of the same class are interfering with each other, that strongly suggests that you are not storing all your state in instance variables. I would suspect that something in ProductDetailViewController stores its state in global or class data, and that your problem lives in there.
It's possible that you have done something silly like making ProductDetailViewController a forced singleton (overriding +alloc, which you should almost never do), but generally people remember when they've done that.
Never call viewDidLoad directly. That's only for the system to call. (I know you were just testing, but don't leave that in.)
But are you calling [super viewDidAppear:animated] in your viewDidAppear:? You have to call your superclass in that method.

Some memory management understanding

I have created a property of a viewController and retaining it from ClassB of viewController (Class A).
so basically I have #property (nonatomic, retain) ClassAViewControllerVC, and synthesized in the main file.
I have an IBAction in which I am allocating ClassAViewController and pushing it on navigation stack, but I am trying to analyze where should I release this viewController?
- (IBAction) response {
ClassAViewControllerVC = [ClassAViewController alloc] initWithNib:#"ClassAViewController" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:self.ClassAViewControllerVC animated:YES]
}
Is it okay to release the view controller after I stack it on the navigation-Controller as described above?
Also, is it a good idea to set property for such viewController at the first place? I started to notice that my apps started to crash if not utilized #property retain way. Any thoughts or concern would be appreciated.
Thanks
Firstly, no, it's not necessary to keep such an object in a property. You only need to keep objects in a property if the class will require access to the object later on. In this case, I think a local variable will do.
In this example, you create a ClassAViewController with alloc, meaning that the caller (this method) has responsibility to release it once it's finished with it.
When you add it to the navigation controller stack, the navigation controller retains it, because it keeps a reference to it.
So, at the end of this method, you should release it, but it's been retained by the navigation controller, so it's not deleted.
The code should look like this:
- (IBAction) response {
ClassAViewController *viewController = [ClassAViewController alloc] initWithNib:#"ClassAViewController"
bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:viewController animated:YES]
[viewController release];
}
P.S. it's convention in objective-C to write variable names starting with a lowercase letter. Uppercase starting letters are used for class names, and it confuses the bejeesus out of me! ;)
if ClassAViewControllerVC is a field of ClassB, you should release it in the dealloc method.
But you should probably create ClassAViewControllerVC in the init or even better viewDidLoad method.(note: if you create something in viewDidLoad, you need to release it viewDidUnload as well, not only in dealloc)
Because now every time "response" method is called ClassAViewControllerVC will be created all over again. (and there is not much sense storing it in the property). If this is what you want you probably should not make ClassAViewControllerVC a filed of your ClassB, but just have it as s local variable in response method like this:
- (IBAction) response {
ClassAViewController *classAViewController = [ClassAViewController alloc] initWithNib:#"ClassAViewController" bundle:[NSBundle mainBundle]];
[self.navigationController pushViewController:classAViewController animated:YES];
[classAViewController release];
}
Best way to solve this problem is, drag and drop empty UIViewController in your ClassB.xib file. Create #property (nonatomic, retain) ClassAViewControllerVC, make the connection to empty UIViewController you just drop to classB.xib file and also change the NIB name to the class A nib file name. This will solve all the problem of crashing because you are not allocating or releasing any memory.

ViewController never gets deallocated

In my mind, myViewController should be deallocated around the time that I pop back to the root view controller with the following code, but I never see the deallocation message getting NSLogged.
If this should work, then what kind of problem can I look for in the myViewController's class that might cause it to get deallocated when I popToRootViewController?
Thanks.
The following gets called in my tableView:
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
{
MyViewController *vc = [[MyViewController alloc] initWithNibName:#"MyViewController" bundle:nil];
[self.navigationController pushViewController:vc animated:YES];
[vc release];
}
UPDATE:
This code was perfect, but it was some bad memory management in my custom view controllers that caused neither to be released. I had some retained properties that should have been assign instead (or at least, that's the way I solved it). See comments for specifics.
When you use poptoviewcontroller then dealloc method will call for the topmost view controller in navigation controller. You can put a breakpoint in dealloc method of your current view controller and when you called popviewcontroller then your dealloc method gets called and release all the stuff/varaibles you have created in your view controller.
#JaySmith02 is right
it was some bad memory management in my custom view controllers that caused neither to be released. I had some retained properties that should have been assign instead (or at least, that's the way I solved it)
In my case the culprit was
#property (nonatomic, retain) id<TTSlidingPagesDataSource> dataSource;
From my viewController when I wrote
slidingPages.dataSource = self
I guess the dataSource retained my viewController and made a circular retention. The 'dealloc' of my viewController was never getting called.
Note: Under ARC dealloc does get called. Difference is you cannot call [super dealloc]
The solution:
#property (nonatomic, assign) id<TTSlidingPagesDataSource> dataSource;

How to release the objects which are in Xib in iphone

In my .h file create 3 objects like below
IBOutlet UIScrollView *scrollView;
IBOutlet UITextView *txtMessage;
IBOutlet UIWebView *webView;
In xib make Connection for all 3 objects
hierarchy in xib like
**View
---UIScrollView
---UITextView
---UIWebView**
then I am printing retainCount in dealloc method
NSLog(#"scrollView retainCount:%d",[scrollView retainCount]);
[scrollView release];scrollView=nil;
NSLog(#"txtMessage retainCount:%d",[txtMessage retainCount]);
[txtMessage release];txtMessage=nil;
NSLog(#"webView retainCount:%d",[webView retainCount]);
[webView release];webView=nil;
on console i am getting like below
scrollView retainCount:3
txtMessage retainCount:2
webView retainCount:2
I want to know why its happens like this ,and one more thing how can release this objects in dealloc method...
The answer is: you don't release them. You didn't alloc] init] then, they're not your responsibility memory-wise.
The only case where they're somehow your responsibility, is when you retain them using a #property (retain) for the IBOutlets. In that case, you'll release them in [dealloc]
Answering to your comment in another answer about instruments and releasing the outlets that the xib creates: if you release them, then the view will be unload sometime and the responsible for releasing the elements in the .xib (which it wasn't you) will release them again, and it will crash ;) The reason why you go back to another view and the memory doesn't decrease is because when a view disappears, it's not unloaded, that only happens when either the view controller is deallocated, or when the application gets a memory warning.
I don't think you need to do anything in dealloc. In Objective-C, if you haven't alloc/retain anything, you don't need to worry.

Do I need to release xib resources?

If I have something like a UILabel linked to a xib file, do I need to release it on dealloc of my view? The reason I ask is because I don't alloc it, which makes me think I don't need to release it either?
eg (in the header):
IBOutlet UILabel *lblExample;
in the implementation:
....
[lblExample setText:#"whatever"];
....
-(void)dealloc{
[lblExample release];//?????????
}
If you follow what is now considered to be best practice, you should release outlet properties, because you should have retained them in the set accessor:
#interface MyController : MySuperclass {
Control *uiElement;
}
#property (nonatomic, retain) IBOutlet Control *uiElement;
#end
#implementation MyController
#synthesize uiElement;
- (void)dealloc {
[uiElement release];
[super dealloc];
}
#end
The advantage of this approach is that it makes the memory management semantics explicit and clear, and it works consistently across all platforms for all nib files.
Note: The following comments apply only to iOS prior to 3.0. With 3.0 and later, you should instead simply nil out property values in viewDidUnload.
One consideration here, though, is when your controller might dispose of its user interface and reload it dynamically on demand (for example, if you have a view controller that loads a view from a nib file, but on request -- say under memory pressure -- releases it, with the expectation that it can be reloaded if the view is needed again). In this situation, you want to make sure that when the main view is disposed of you also relinquish ownership of any other outlets so that they too can be deallocated. For UIViewController, you can deal with this issue by overriding setView: as follows:
- (void)setView:(UIView *)newView {
if (newView == nil) {
self.uiElement = nil;
}
[super setView:aView];
}
Unfortunately this gives rise to a further issue. Because UIViewController currently implements its dealloc method using the setView: accessor method (rather than simply releasing the variable directly), self.anOutlet = nil will be called in dealloc as well as in response to a memory warning... This will lead to a crash in dealloc.
The remedy is to ensure that outlet variables are also set to nil in dealloc:
- (void)dealloc {
// release outlets and set variables to nil
[anOutlet release], anOutlet = nil;
[super dealloc];
}
I found what I was looking for in the Apple docs. In short you can set up your objects as properties that you release and retain (or just #property, #synthesize), but you don't have to for things like UILabels:
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/chapter_3_section_4.html#//apple_ref/doc/uid/10000051i-CH4-SW18
The
[anOutlet release], anOutlet = nil;
Part is completely superfluous if you've written setView: correctly.
If you don’t release it on dealloc it will raise the memory footprint.
See more detail here with instrument ObjectAlloc graph
Related: Understanding reference counting with Cocoa / Objective C
You do alloc the label, in a sense, by creating it in IB.
What IB does, is look at your IBOutlets and how they are defined. If you have a class variable that IB is to assign a reference to some object, IB will send a retain message to that object for you.
If you are using properties, IB will make use of the property you have to set the value and not explicitly retain the value. Thus you would normally mark IBOutlet properties as retain:
#property (nonatomic, retain) UILabel *lblExample;
Thus in ether case (using properties or not) you should call release in your dealloc.
Any IBOutlet that is a subview of your Nib's main view does not need to be released, because they will be sent the autorelease message upon object creation. The only IBOutlet's you need to release in your dealloc are top level objects like controllers or other NSObject's. This is all mentioned in the Apple doc linked to above.
If you dont set the IBOutlet as a property but simply as a instance variable, you still must release it. This is because upon initWithNib, memory will be allocated for all IBOutlets. So this is one of the special cases you must release even though you haven't retained or alloc'd any memory in code.