Sometimes I see the following code into two different formats:
Format 1:
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController {
IBOutlet UILabel *myText;
}
#property (retain, nonatomic) UILabel *myText;
-(IBAction)buttonPressed:(id)sender;
#end
Format 2:
#import <UIKit/UIKit.h>
#interface MyViewController : UIViewController {
}
#property (retain, nonatomic) IBOutlet UILabel *myText;
-(IBAction)buttonPressed:(id)sender;
#end
which is the correct format? Why?
To clarify what Hack Saw said, and more directly answer your question, it does not matter whether you put IBOutlet in your property declaration or your instance variable declaration.
What Hack Saw was trying to say is that IBOutlet and IBAction both mean nothing to the compiler (IBAction gets compiled into void). The only reason they are there is for Interface Builder to parse the file and make a list of all objects and methods that you the developer says it should care about.
IBOutlet is a marker for interface builder to find your declarations, and make them available in the drop downs in IB.
They are strictly only required if you want to have IB connect an IB object to a reference in your code, for instance, connecting a button to a UIButton * declaration.
So, the basic idea here is that Interface Builder has a list of objects it knows how to make. You could make those objects in code, but a lot of the time, you don't need more capability than what IB offers, which is actually quite a lot.
In those cases, IB takes care of that object entirely. It allocates it, and sets the various parameters, and takes care of displaying it.
However, you obviously need to be able to talk to it, as well, most of the time. In order to do this, your declare a pointer to the object, like UIButton *mybutton, but in order to let IB know you want to connect up with it, you add IBOutlet to the declaration.
IB lists the variable, you connect the button up to something in File's Owner, or sometimes firstresponder, and then IB saves that connection data, and sets everything up when the nib gets loaded.
Related
I'm an Objective-C newbie and I'm reading "iPhone programming" by Alasdair Allan. While reading, I found this code:
#interface RootController : UIViewController <UITableViewDataSource, UITableViewDelegate> {
UITableView *tableView;
NSMutableArray *cities;
}
// warning: remember this tableView
#property (nonatomic, retain) IBOutlet UITableView *tableView;
The relative implementation starts this way:
#implementation RootController
#synthesize tableView;
Now: I learnt that #synthesize is a sort of shortcut to avoid boring getters and setters.
But I've some question:
in the code of the implementation tableView is never explicitly called but the dealloc releases it;
if it never gets called explicitly why the #synthesize?
Is it mandatory for IBOutlets to be synthesized?
From Memory Management of Nib Objects,
When a nib file is loaded and outlets established, the nib-loading mechanism always uses accessor methods if they are present (on both Mac OS X and iOS). Therefore, whichever platform you develop for, you should typically declare outlets using the Objective-C declared properties feature.
For iOS, you should use:
#property (nonatomic, retain) IBOutlet UIUserInterfaceElementClass *anOutlet;
You should then either synthesize the corresponding accessor methods, or implement them according to the declaration, and (in iOS) release the corresponding variable in dealloc.
in the code of the implementation tableView is never explicitly called but the dealloc releases it;
That is because when you do assign a value to the tableView, your controller retains it, and it will need to release it when it gets dealloc'd. Don't forget, #properties declared in an interface are publicly accessible. In your case specifically, the tableView you're declaring as IBOutlet is initialized by the view controller loadView method using the connections you define in Interface Builder between the File's Owner and the UITableView.
if it never gets called explicitly why the #synthesize?
You need to provide accessors for all declared #properties. They can be #synthesized, or you could write your own.
Is it mandatory for IBOutlets to be synthesized?
No, but it's way more convenient that way. The rule enforced by the compiler is that #properties must have corresponding accessors (synthesized or not) in the implementation.
For reference: From Xcode 4.4 and LLVM Compiler 4.0 on the #synthesize directive is no longer required as it will be provided by default for #properties defined in the interface.
If you type
#property (nonatomic, retain) IBOutlet UITableView *tableView;
you tell the compiler: "Listen, there will be a getter and a setter. If appropriate, use them!" And it will use them when loading the nib.
Therefore you have to implement the getter and the setter otherwise the compiler will complain.
The IBoutlet pseudo-type is just a marker so that the InterfaceBuilder "knows" that the mentioned class-file has a handle/outlet to the UITableView instance.
When compiling IBOutlet is being removed by the preprocessor (InterfaceBuilder parses (looks at) the source files). It's similar with IBAction: it is being replaced with void by the preprocessor.
That said, you could use the reference to said instance to do stuff programmatically (Like adding/changing values of the UITableView)
In the following code what is the function of -(IBAction)setLabelPushed:(id)sender;
#import <UIKit/UIKit.h>
#interface BasicIPhoneAppViewController : UIViewController
{
IBOutlet UILabel *myLabel;
IBOutlet UITextField *myTextField;
}
-(IBAction)setLabelPushed:(id)sender;
#end
Actually it is a non-static method. IBAction means that it can be used as a event handler in Interface Builder (it can be linked to some action). You should provide more details, for example the body of setLabelPushed function.
get the value from textfield and show it in the label .I think so......
It is a method you can bind from within the Interface Builder...
http://mobile.tutsplus.com/tutorials/iphone/iphone-sdk-interface-builder-basic-training/
IBAction resolves to "void" and IBOutlet resolves to nothing, but they signify to Xcode and Interface builder that these variables and methods can be used in Interface builder to link UI elements to your code.
If you're not going to be used Interface Builder at all, then you don't need them in your code, but if you are going to use it, then you need to specify IBAction for methods that will be used in IB and IBOutlet for objects that will be used in IB. IBOutlet and IBAction
i recently updated my IDE to XCode 4.0 and saw a strange change in the Utillity-Application boiler-plate-code:
First, the MainViewController.h-File:
#import <UIKit/UIKit.h>
#class MainViewController;
#interface UtilityAppDelegate : NSObject <UIApplicationDelegate> {
}
#property (nonatomic, retain) IBOutlet UIWindow *window;
#property (nonatomic, retain) IBOutlet MainViewController *mainViewController;
#end
Question 1: Where is "mainViewController" declared in the first place? I didn't find it anywhere.
In the *.m-File there is a
#synthesize mainViewController=_mainViewController;
statement. So my second question: Where is "_mainViewController" hidden? Can't find a declaration anywhere. It comes somehow out of the main *.nib file I guess.
But there is another problem: I did add a UINavigationController to one of my recent projects and have no need for mainViewController anymore. But when I delete #property and #synthesize out of MainViewController.m/.h , I can't run the app anymore because of this exception:
setValue:forUndefinedKey:]: this class is not key value coding-compliant for the key mainViewController.'
occurring at this line
int retVal = UIApplicationMain(argc, argv, nil, nil);
in the main.m.
Thx for your help.
Look at you info.plist it's should be declared there. If you created your app with a template it's configured by the plist. There is some implicit declaration done by this, the mainWindow.xib(in my example) contains more than a window. There are the connections to the appDelegate and the viewController, see second screenshot.
The last line of the screenshot:
Question 1:
When you use a declaration like this, you don't also need to explicitly define the property.
.h
#property (nonatomic, retain) IBOutlet MainViewController *mainViewController;
.m
#synthesize mainViewController=_mainViewController;
Question 2:
_mainViewController is not hidden. It points to mainViewController which is implicitly defined in the #property statement in .h
#synthesize mainViewController=_mainViewController;
This format is used to distinguish between the ivar and other properties. It refers to mainViewController.
Question 3:
You deleted the declarations #property/#synthesize for mainViewController, but it still exists in the nib file (IB). Delete it from IB and you should be good to go.
You're encountering the new ABI for the first time. It is no longer necessary to actually declare variables for properties. If you use #property and #synthesize, a backing ivar will automatically be generated for you.
You're probably getting the KVC error because the NIB still references the old property. You should see a warning about this during compile. In IB, look at your App Delegate; it probably still has an outlet for mainViewController, and you probably are still generating a MainViewController. You need to delete them from the NIB.
The context: I have three views. One Introductory view, an Upload view and the Main view. As classes (With their respective headers) I have the rootViewController (SwitchViewController), IntroViewController and UploadViewController. The first view to be shown is IntroView. The user presses a button (declared in SwitchViewController) that takes them to the UploadView, then in the UploadView they get to choose an image and press the button again to go back to IntroView.
The thing is that while the user gets to pick the image with UIImagePickerController the button to switch views won't hide nor a UIImageView I have with a logo on top of the view(screen). The UIImageView and the UIButton are both declared in SwitchViewController's header.
The code used:
UploadViewController.h
#import [...] //Imports
#class SwitchViewController;
#interface UploadViewController :
UIViewController <UIImagePickerControllerDelegate,
UINavigationControllerDelegate,UIActionSheetDelegate> {
UITextField *imageTextField;
UIImageView *uploadedImage;
SwitchViewController *switchViewController;
[...]
}
#property (nonatomic, retain) SwitchViewController *switchViewController;
#property (nonatomic, retain) IBOutlet UITextField *imageTextField;
#property (nonatomic, retain) IBOutlet UIImageView *uploadedImage;
[...]
#end
UploadViewController.m
[...]
- (IBAction) selectImageButtonPressed {
self.switchViewController.submitButton.hidden = YES;
self.switchViewController.imageLogo.hidden = YES;
[...] //continues
I just begun recently programming in objective-c so please forgive me if the question is very essential. I have looked and am following "Beginning iPhone 3 Development" of APRESS. But even if it helps to greatly understand the basics sometimes I get lost.
PS: If it is clearer to answer the question the SwitchViewController.h and .m snippet codes can be provided if asked. But I thought this text is big as it is.
#Joze i think I may have understood your problem switchViewController is a variable of the class UploadViewController so if you do anything with that variable it wont affect the switchViewController view. so when you are calling the switchViewController view at that time you have to do initWithNibName: bundle: and then hide the button and imageView and also you need to do something like switchViewController.delegate = self; and then call the view modally or what ever way you want it.
PS. i m not sure the that spelling is correct. i dont have xcode at my home.
I hope your problem solves with this.
I solved my problem after refactoring the whole code and changing the general structure of the program itself. Now I have 3 views and each with a viewController to control it. All the switching of views occurs in the Delegate since he has access to everyone. That way I can control every property with every controller, without much difficulty. Changing the property of one of the objects present in one view from another view is difficult and rather inconvenient if not sometimes impossible.
The approach I took when asking this question was short sighted for the application that had to be done. I thank all those who tried to help.
If I do this:
#interface RegisterController : UIViewController <UITextFieldDelegate>
{
IBOutlet UITextField *usernameField;
}
instead of this:
#interface RegisterController : UIViewController <UITextFieldDelegate>
{
UITextField *usernameField;
}
#property (nonatomic, retain) IBOutlet UITextField *usernameField;
Will something bad happen? I know in the second case, the field is retained, but does this make a different since the nib owns the field? Will the field go away without the retain? and under what circumstances? The code in the first case works, was wondering whether this is an issue or not in terms of memory management.
It is recommended you declare properties for all of your IBOutlets for clarity and consistency.
The details are spelled out in the Memory Management Programming Guide. The basic gist is, when your NIB objects are unarchived, the nib loading code will go through and set all of the IBOutlets using setValue:forKey:. When you declare the memory management behavior on the property, there is no mystery as to what is going on. If the view gets unloaded, but you used a property that was declared as retain, you've still got a valid reference to your textfield.
Perhaps a more concrete example would be useful to indicate why you should use a retaining property:
I'm going to make some assumptions about the context in which you're working--I'll assume the UITextField above is a subview of another view that is controlled by a UIViewController. I will assume that at some point, the the view is off the screen (perhaps it is used in the context of a UINavigationController), and that at some point your application gets a memory warning.
So lets say your UIViewController subclass needs to access its view to display it on screen.
At this point, the nib file will be loaded and each IBOutlet properties will be set by the nib loading code using setValue:forKey:. The important ones to note here are the top level view that will be set to the UIViewController's view property, (which will retain this top level view) and your UITextField, which will also be retained. If it is simply set, it'll have a retain put on it by the nib loading code, otherwise the property will have retained it. The UITextField will also be a subview of the top level UIView, so it will have an additional retain on it, being in the subviews array of the top level view, so at this point the text field has been retained twice.
At this point if you wanted to switch out the text field programmatically, you could do so. Using the property makes memory management more clear here; you just set the property with a new autoreleased text field. If you had not used the property, you must remember to release it, and optionally retain the new one. At this point it is somewhat ambiguous as to whom owns this new text field, because the memory management semantics are not contained within the setter.
Now let's say a different view controller is pushed on the UINavigation Controller's stack, so that this view is no longer in the foreground. In the case of a memory warning, the view of this offscreen view controller will be unloaded. At this point, the view property of the top level UIView will be nulled out, it will be released and deallocated.
Because the UITextField was set as a property that was retained, the UITextField is not deallocated, as it would have been had its only retain been that of the subviews array of the top level view.
If instead the instance variable for the UITextField not been set via a property, it'd also be around, because the nib loading code had retained it when setting the instance variable.
One interesting point this highlights is that because the UITextField is additionally retained through the property, you'll likely not want to keep it around in case of a memory warning. For this reason you should nil-out the property in the -[UIViewController viewDidUnload] method. This will get rid of the final release on the UITextField and deallocate it as intended. If using the property, you must remember to release it explicitly. While these two actions are functionally equivalent, the intent is different.
If instead of swapping out the text field, you chose to remove it from the view, you might have already removed it from the view hierarchy and set the property to nil, or released the text field. While it is possible to write a correct program in this case, its easy to make the error of over-releasing the text field in the viewDidUnload method. Over-releasing an object is a crash-inducing error; setting a property that is already nil again to nil is not.
My description may have been overly verbose, but I didn't want to leave out any details in the scenario. Simply following the guidelines will help avoid problems as you encounter more complex situations.
It is additionally worth noting that the memory management behavior differs on Mac OS X on the desktop. On the desktop, setting an IBOutlet without a setter does not retain the instance variable; but again uses the setter if available.
Declaring something IBOutlet, from a memory management standpoint, does nothing (IBOutlet is literally #defined as nothing). The only reason to include IBOutlet in the declaration is if you intend to connect it in Interface Builder (that's what the IBOutlet declaration is for, a hint to IB).
Now, the only reason to make an #property for an instance variable is if you intend to assign them programatically. If you don't (that is, you're only setting up your UI in IB), it doesn't matter whether you make a property or not. No reason to, IMO.
Back to your question. If you're only setting this ivar (usernameField) up in IB, don't bother with the property, it won't affect anything. If you DO make a property for usernameField (because you're programatically creating it), definitely do make a property for it, and absolutely DO make the property retain if so.
In fact there are two models:
THE OLD MODEL
These model was the model before Objective-C 2.0 and inherited from Mac OS X. It still works, but you should not declare properties to modify the ivars. That is:
#interface StrokeWidthController : UIViewController {
IBOutlet UISlider* slider;
IBOutlet UILabel* label;
IBOutlet StrokeDemoView* strokeDemoView;
CGFloat strokeWidth;
}
#property (assign, nonatomic) CGFloat strokeWidth;
- (IBAction)takeIntValueFrom:(id)sender;
#end
In this model you do not retain IBOutlet ivars, but you have to release them. That is:
- (void)dealloc {
[slider release];
[label release];
[strokeDemoView release];
[super dealloc];
}
THE NEW MODEL
You have to declare properties for the IBOutlet variables:
#interface StrokeWidthController : UIViewController {
IBOutlet UISlider* slider;
IBOutlet UILabel* label;
IBOutlet StrokeDemoView* strokeDemoView;
CGFloat strokeWidth;
}
#property (retain, nonatomic) UISlider* slider;
#property (retain, nonatomic) UILabel* label;
#property (retain, nonatomic) StrokeDemoView* strokeDemoView;
#property (assign, nonatomic) CGFloat strokeWidth;
- (IBAction)takeIntValueFrom:(id)sender;
#end
In addition you have to release the variables in dealloc:
- (void)dealloc {
self.slider = nil;
self.label = nil;
self.strokeDemoView = nil;
[super dealloc];
}
Furthermode, in non-fragile platforms you can remove the ivars:
#interface StrokeWidthController : UIViewController {
CGFloat strokeWidth;
}
#property (retain, nonatomic) IBOutlet UISlider* slider;
#property (retain, nonatomic) IBOutlet UILabel* label;
#property (retain, nonatomic) IBOutlet StrokeDemoView* strokeDemoView;
#property (assign, nonatomic) CGFloat strokeWidth;
- (IBAction)takeIntValueFrom:(id)sender;
#end
THE WEIRD THING
In both cases, the outlets are setted by calling setValue:forKey:. The runtime internally (in particular _decodeObjectBinary) checks if the setter method exists. If it does not exist (only the ivar exists), it sends an extra retain to the ivar. For this reason, you should not retain the IBOutlet if there is no setter method.
There isn't any difference between the way those two interface definitions work until you start using the accessors provided by the property.
In both cases, you'll still need to release and set-to-nil the IBOutlet in your dealloc or viewDidUnload methods.
The IBOutlet points to an object instantiated within a XIB file. That object is owned by the File's Owner object of the XIB file (usually the view controller that the IBOutlet is declared in.
Because the object is created as a result of loading the XIB, it's retain count is 1 and is owned by your File's Owner, as mentioned above. This means that the File's Owner is responsible for releasing it when it's deallocated.
Adding the property declaration with the retain attribute simply specifies that the setter method should retain the object passed in to be set - which is the correct way to do it. If you did not specify retain in the property declaration, the IBOutlet could possibly point to an object that may not exist any more, due to it being released by its owner, or autoreleased at some point in the program's lifecycle. Retaining it prevents that object being deallocated until you're done with it.
Objects in the nib file are created with a retain count of 1 and then autoreleased. As it rebuilds the object
hierarchy, UIKit reestablishes connections between the objects using setValue:forKey:, which uses the
available setter method or retains the object by default if no setter method is available. This means that any object for which you have an outlet remains valid. If there are any top-level objects you do not store in outlets, however, you must retain either the array returned by the loadNibNamed:owner:options: method or the objects inside the array to prevent those objects from being released prematurely.
Well, in the second case you're adding a getter/setter method for that particular IBOutlet. Any time you add a getter/setter method you (almost always) want to have it set to retain for memory management issues. I think a better way to have posed you're question would have been this:
#interface RegisterController : UIViewController <UITextFieldDelegate>
{
IBOutlet UITextField *usernameField;
}
#property (nonatomic) IBOutlet UITextField *usernameField;
or
#interface RegisterController : UIViewController <UITextFieldDelegate>
{
IBOutlet UITextField *usernameField;
}
#property (nonatomic, retain) IBOutlet UITextField *usernameField;
In that case, then yes, you would need to add a retain since it will affect memory management. Even though it may not have any effects, if you're programatically adding and removing IBOutlet's, you could potentially run into issues.
As a general rule: always add an #property (with retain) whenever you have an IBOutlet.