release view in viewDidUnload - cocoa-touch

I know that if I retain an IBOutlet by using property then I have to set it to nil in viewDidUnload but what about others?
For example, i have three subviews view1, view2 and view3, that load from nib and that is the controller's header file
#interface MyViewController : UIViewController {
IBOutlet UIView *view1;
UIView *view2;
//no reference for view3
}
#property (nonatomic, retain) IBOutlet UIView *view2; //property view2 is an IBOutlet!!
#end
and method viewDidUnload
- (void)viewDidUnload {
self.view2 = nil;
//[view1 release];
//view1 = nil;
[super viewDidUnload];
}
do I have to release view1 and set it to nil? or UIViewController will set it to nil for me?
what about view3?
also do I have to release view1 in dealloc?
edit:
I think many people does not understand my question
Firstly, view1 is an IBOutlet which declared as an ivar and assign an ivar will not retain it. I know that UIViewController definitely will retain it but do i have to release it or UIViewController will release it for me? If UIViewController will release it then there is no point that i have to release it again.
Secondly, view2 is also an IBOutlet although it is declared as a property not ivar.
#property (nonatomic, retain) IBOutlet UIView *view2;
It is a retain property, therefore set it will retain it so I know that I have to set it to nil in order to release it. I have no problem about it.
For view3, there is no reference for it, therefore I am assuming I don't have to do anything about it. I also assuming there is no need to make a reference for every object in nib.

All outlets are retained by default even if they don't have a property declared for them. So you will need to release them. If you go on to declare an assigned property as an outlet, then you don't need to release but you can't rely on it either as you are not an owner.
So you need to release both view1 and view2 as they are declared as outlets. view3 is a variable that doesn't exist and hence needn't be worried about.

When a nib is loaded, all its objects are automatically instantiated and retained.
The file's owner of your nib file is then the owner of your UIView.
If you use UIView *view2 you can't connect them using interface builder.
So that doesn't make really sense to me.
You have to release in dealloc as well.
- (void)viewDidUnload {
self.view1 = nil;
self.view2 = nil;
[super viewDidUnload];
}
- (void)dealloc {
[self.view1 release];
self.view1 = nil;
[self.view2 release];
self.view2 = nil;
}

Related

IBOutlet is nil after initWithCoder is called

Simple problem, I have defined a UIImageView, called bigImageView in a UIViewController using the storyboard,
It's declared in the h file of that UIViewController as follows:
#property (retain, nonatomic) IBOutlet UIImageView *bigImageView;
on my appDelegate I init the UIViewController as follows:
imageViewController = [storyboard instantiateViewControllerWithIdentifier:#"chosenImageController"];
this calls initWithCoder on my UIViewController m file:
-(id)initWithCoder:(NSCoder *)aDecoder
{
self = [super initWithCoder:aDecoder];
if (self) {
// Custom initialization
}
return self;
}
This function is only called once so there's no double init.
However, later, when I check my bigImageView pointer, it's still nil.
Isn't the init supposed to allocate memory to it?
I think that's why when I try to set this UIImageview to hold a UIImage it doesn't display the image
Thanks
It's all working how it's meant to. First every object in the nib/storyboard gets alloc/init called on them, then all the connections are made, and then viewDidLoad is called.
You need to wait for - (void)viewDidLoad to be called on your controller, and then bigImageView should be set. If it's not set then you did something wrong in the storyboard.
init methods are not responsible for allocating any memory. All memory is allocated by the alloc method which is always called before init. Alloc will fill all your instance variables with nil/NULL/0 values, and then init gives the chance to assign initial values to each one (based on the contents of the NSCoder object usually, but it's up to you to decide what should be done).
For IB outlets however, those are setup by the nib loading process after init.
EDIT:
// ViewControllerA.m:
imageViewController = [storyboard instantiateViewControllerWithIdentifier:#"chosenImageController"];
imageViewController.image = imageToShow;
// ViewControllerB.h
#property (retain) NSImage *image;
#property (retain, nonatomic) IBOutlet UIImageView *bigImageView;
// ViewControllerB.m
- (void)viewDidLoad
{
self.bigImageView.image = self.image;
[super viewDidLoad];
}
You don't need to define initWithCoder, since you have no custom logic in there. I would delete that boilerplate code.
Here is what I would check:
In the storyboard, ensure that the class of the view controller is set properly.
Ensure that the outlet is hooked up properly in the storyboard by looking for a circle near your #property. It should be a filled in circle, not an outline of a circle.
Make sure you are reading the value only after viewDidLoad is called. Apple's only guarantee is that the outlet is set after this method call.
Update: It sounds like you want to access the image view before the view is loaded. There is no way to do this. One hack is to call viewController.view which will force the view to load, but there are many reasons why you should not do this.
A better approach would be to implement properties on your view controller which work for both when the view is not loaded and when the view is loaded. You can see an example of an elegant solution in this question. Notice how if the view is loaded, the photographerLabel will get set via the didSet method. On the other hand, if the view is not loaded, it will get set via the viewDidLoad method. For an Objective-C version of that code or for more details, see the linked video in that question.

Maintain a Link to Subviews in View Controller

Here's my situation: I have a UIViewController that manages a hierarchy of subviews, perhaps as shown below:
This view is built from a .xib. I would like to be able to maintain access to each subview of topView – that is, I want a pointer to each so that I can, for example, say something like:
[button1 setText:#"Hello!"];
Usually, to do this, I wire up each element to which I would like access using Interface Builder, resulting in a header that looks something like this:
#interface MyViewController : UIViewController
{
__weak IBOutlet UIView *view;
__weak IBOutlet UILabel *label;
__weak IBOutlet UIButton *button1;
__weak IBOutlet UIButton *button2;
}
#end
These instance variables are __weak, which is fine, since by the time my view controller "gets" them, they're already owned by my view controller's root view (which, confusingly, I labeled "topView" in my quick diagram). In fact, I want these references to be weak – when my root view is released, so too should all its subviews be released. Great.
But let's say I want to create a new element of the UI, maybe a custom button, entirely in code. I'll call this element CustomViewClass, which will be subclassed from UIView. The instance of CustomViewClass that I will create will be called customButton. As with the other subviews of my view, I would like "access" to customButton so that I can interact with it. I know, however, that like any other subview, customButton will be owned by its superview, and that's how it should be – again, I want it to be released whenever my view is released. This makes me think that I should declare this view as a __weak instance variable or property of my view controller. Let's do that:
#interface MyViewController : UIViewController
{
__weak IBOutlet UIView *view;
__weak IBOutlet UILabel *label;
__weak IBOutlet UIButton *button1;
__weak IBOutlet UIButton *button2;
__weak CustomViewClass *customButton;
}
#end
Then, in my implementation:
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
customButton = [[CustomViewClass alloc] init];
[[self view] addSubview:customButton];
}
#end
As you've probably already realized, this won't work, and the compiler will throw a warning to boot. Something like:
Assigning retained object to weak variable; object
will be released after assignment
I currently dodge this sort of warning with some very poor style:
#implementation MyViewController
- (void)viewDidLoad
{
[super viewDidLoad];
CustomViewClass *customButtonLocal = [[CustomViewClass alloc] init];
[[self view] addSubview:customButtonLocal];
customButton = customButtonLocal;
}
#end
That way, I get what I want:
An instance of CustomViewClass on the screen...
...with exactly one owner, its superview...
...and no lingering variables (customButtonLocal is released immediately after the block ends).
But this can't be the "right" way to do this. So, finally, my question:
How should I be allocating and instantiating this programmatically-created __weak variable without using this middle-man workaround?
Make CustomViewClass *customButton a strong reference.
The reason why you usually declare variables for your subviews as __weak IBOutlet is that the existence of these links does not imply ownership. Subviews are owned by the object instantiated from a NIB/Storyboard. You own that object directly, and you also own its dependent objects indirectly.
The customButton is a different story: you create it programmatically, so your NIB/Storyboard does not own it. Therefore, you should make the reference to it __strong (which is the default when there are no ARC modifiers).

IBOutlets properties. Release or not release

I'm developing an iOS 4 application.
I have this ViewController:
#interface BlogViewController : UIViewController
{
...
UIView* tabBar;
}
#property (nonatomic, retain) IBOutlet UIView* tabBar;
And its implementation:
And its implementation:
#implementation BlogViewController
#synthesize tabBar;
- (void) dealloc
{
...
[super dealloc];
}
- (void)viewDidUnload
{
[super viewDidUnload];
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
self.tabBar = nil;
}
My question if I have a IBOutlet property, is it necessary to declare the UIView like this?
#interface BlogViewController : UIViewController
{
...
UIView* tabBar;
}
If I do it, Do I need to release it on dealloc?
- (void) dealloc
{
...
[tabBar release];
[super dealloc];
}
In order: no you don't need to declare the instance variable, yes you do need to release the object. You may consider using Automatic Reference Counting to get the memory management aspect correct for you.
Yes, you do need to release an IBOutlet, if you retained it.
However, IBOutlets are owned by their nib file, so the usual practice is to use an assign or weak #property instead of retain or strong. In which case you don't need to release it.
If you are using xcode 4+ then while creating the outlet it itself makes a release for the same in dealloc and viewDidUnload so no need to do it again.

Error when ViewController is implementing UITextFieldDelegate

When implementing the UITextFieldDelegate in my ViewController class, the following error is thrown when entering the first character in the text field:
-[MyViewController respondsToSelector:]: message sent to deallocated instance...
So, I tried creating a separate class (inheriting only NSObject) and implementing UITextFieldDelegate. Guess what, it worked perfectly. However, that introduces some other problems as I have to do a lot of ugly cross-class-communication that I'd like to avoid. Here's the relevant parts of my app delegate code:
#interface RMSAppDelegate : NSObject <UIApplicationDelegate,
UITabBarControllerDelegate>
#property (nonatomic, retain) UIViewController* myViewController;
#end
#implementation MyAppDelegate
#synthesize myViewController;
- (BOOL)application:(UIApplication *)application
didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
myViewController = [[MyViewController alloc]
initWithNibName:#"MyView" bundle:nil];
[self.window setRootViewController:myViewController];
[self.window makeKeyAndVisible];
return YES;
}
#end
.. and here's what is being displayed:
#interface MyViewController : UIViewController <UITextFieldDelegate>
#property (nonatomic, retain) IBOutlet UITextField* pinTextField;
- (void)viewDidLoad;
#end
#implementation MyViewController
#synthesize pinTextField;
- (void)viewDidLoad {
// DOES NOT WORK (WHY?)
//[pinTextField setDelegate:self];
// WORKS, BUT I'D LIKE TO AVOID
[pinTextField setDelegate:[[[MyTextFieldDelegate alloc] init] autorelease];
[pinTextField becomeFirstResponder];
[super viewDidLoad];
}
#end
And please, if you see any code (even off topic) that I could be doing better, leave a comment.
Since you asked for off-topic code comments: You forget to call [super viewDidLoad]. You also don't need to redeclare the prototype in order to override it. And the #synthesize textFieldDelegate is not valid, as you have no property in the class named textFieldDelegate. And your dealloc method is releasing an ivar named tfd which doesn't seem to actually exist in the class.
Your real problem is that you are not properly retaining the view controller at whatever point you allocate it. It may be that the view controller is being instantiated in a nib and associated with an ivar rather than a property declared retain, or is not being associated with anything. Or it could be that you are allocating it in code, adding its view as a subview of something, and then releasing it without ever retaining the view controller itself. Or it could just be that you are just releasing it when you shouldn't.
Your other class works specifically because you are leaking the object, so it never gets deallocated. The better solution, were you to go with this method, would be to store the object in an ivar when you allocate it and then release it (and set the ivar to nil) in both dealloc and viewDidUnload.
Okay, I finally solved this on my own. I have not changed the code. My NIB (.xib) was the culprit!
I thought that nested UIViewControllers was OK, and I still think they are in some cases (and maybe using another programmatic method). Anyway, I was initializing my class MyViewController with a NIB that in the Objects panel had a UIViewController as the first object.
I solved this by having the UIView as the first object in the Objects panel, and setting the File's Owner to be the UIViewController instead.
Correct code, incorrect NIB. Thank you for your help.

How to Unload a sub-view?

I've got a UITableView which loads a view when the accessory button is tapped:
MyView *newView = [[MyView alloc] ...];
[self.navigationController pushViewController:newView];
Inside the view, when the user performs a certain action, I return back to the UITableView by doing this:
[self.navigationController popViewControllerAnimated:YES];
This works, but the next time the view is accessed by tapping the accessory button, it retains some of the values that it contained the first time the view used.
Is there a proper way to actually "completely unload" the view so that it's fresh the next time it is displayed?
You should autorelease the newView (which I assume is actually a View Controller, since you're pushing it to a navigation stack).
As it currently stands your view controller is never being deallocated and is being leaked:
It is allocated (retain count 1)
Pushed into a navigation stack which retains the view controller (retain count 2)
It is then popped from the stack, which releases it (retain count 1)
It is never released again after that
As for why you're getting previous values, you must also be pushing the same instance rather than creating a new one each time you push it to the stack - I can't tell since you haven't posted all of the relevant code.
You need to release any retained subviews of the main view on viewDidUnload...
For instance if you have retained UI elements by declaring them as outlets in IB, set them to nil before releasing them on dealloc.
Here's an example:
#interface ...
{
IBOutlet UIButton *button;
IBOutlet UITextLabel *label;
IBOutlet UITableView *tableview;
}
#property (nonatomic, retain) UIButton *button;
...
Then you should set these to nil on viewDidUnload
- (void)viewDidUnload {
self.button = nil;
self.textlabel = nil;
self.tableview = nil;
...
}