I have a subclass of UIView that's instantiated in a XIB file. I need it to do some initialization (settings some variables and creating a subview).
However, I do not always instantiate this view via Interface Builder. I do it programmatically too. In both cases, the initialization needs to be the same.
My designated initializer is initWithValues:.
The question is; where do I perform the initialization?
Since I have to perform it in 2 different locations, I figured I need to refactor it in a separate initialize method (or something like that), and call it from initWithValues:.
But when loading from IB, both initWithCoder: and awakeFromNib are called. From which method do I have to call initialize? Or do I have to call initWithValues: from initWithCoder: and do nothing in awakeFromNib?
You should use initWithFrame: when initializing views (since it's the designated initializer). Hence, if you have initWithValues: make sure you call initWithFrame: from it.
Something like this should work for initializing: ;)
- (void)initialize{
//init your ivars here
}
- (id)initWithCoder:(NSCoder *)aCoder{
if(self = [super initWithCoder:aCoder]){
[self initialize];
}
return self;
}
- (id)initWithFrame:(CGRect)rect{
if(self = [super initWithFrame:rect]){
[self initialize];
}
return self;
}
I was going to add a further explanation, but mplappert's answer is clear enough. Use awakeFromNib if necessary.
That depends on what you need to initialize. As soon as awakeFromNib gets called, all outlets and action connections of your view are established which is not the case in initWithCoder:. So if you need to rely on those connections, use awakeFromNib. Otherwise you can safely do all your initializing in initWithCoder:.
Unfortunately, the above answers don't take into account these things:
- (void) awakeAfterUsingCoder - and the fact it's called after anything is created by the coder (once for every Xib view).
awakeFromNib suffers from the same fate, I've noticed. (The reason I found this)
Another initializing issue is that initWithCoder and initWithFrame can be avoided for custom views. And if they are called, lazy loading (though not as important on views themselves), means you "might" be able to modify values. I believe I've done so in initWithCoder, but if you then initialize values in awakeFromNib, it's undone at least once.
I've gone so far as to:
- (void) awakeFromNib (or didMoveToSuperView);
{
BOOL called = NO;
if(!called)
{
called = YES;
}
}
Another method I use is to simply call the initializer needed, then call my own class or superclass-specific initializer.
I, too, am looking for a dependable one-time place I can rely on. Until then, I hope my headaches save the next person an hour or so.
Steve
Related
I use delegates to run some methods.
I got class webrequests with 2 delegates: menuVC and mapVC.
When I in mapVC webrequests is accomplish delegates methods of mapVC class.
When I in menuVC webrequests is tried to accomplish delegates methods of mapVC class and crashed app.
Method in webrequests do not helps
if ([self.delegate isKindOfClass: [MenuViewController class]])
{
self.delegate = [MapViewController class];
}
Why it happens?
I use in both classes
self.webRequests = [WebRequests sharedInstance];
self.webRequests.delegate = self;
Original answer:
You can only have one delegate of an object at one time, unless the object is designed to allow for different delegate protocols (e.g. UITableView has both a "delegate" AND a "datasource").
You also can not set your delegate to a non-instantiated object like what you are doing on this line:
self.delegate = [MapViewController class];
This needs to be an actual allocated and instantiated object.
Like "self.mapViewController" that you just pushed or created.
For this question, you probably need to better explain what you are ultimately trying to do, since it looks like your current delegate is a MenuViewController object and you're trying to switch the delegate to a MapViewController object.
New answer:
Since you want things to happen in two different view controllers, the best way to do what you want to do is use a "NSNotification" (which allow multiple objects to observe -- or watch -- for things happening).
Here is a tutorial you can look at, to get a good start with it.
I ask this question because it appears that viewDidLoad gets called before the main block of the initializer does and the class variables I'm initializing in the initializer are turning up nil in viewDidLoad. For your reference, I'm doing this entire viewcontroller programmatically and I've created a custom initializer so I can take in various parameters to be used in my viewcontroller. My custom initializer calls UIViewControllers designated initializer of course.
Basically, I'm curious about what is the proper design pattern for sending in parameters to a UIViewController? I've read other threads about this and haven't really gotten a definitive answer. Should I just skip the initializer and set the properties manually (from outside the class)? That seems kind of bleh, I'd really like to send in some parameters and am curious how others go about doing this?
The reason viewDidLoad is called before initialization completes, is probably because you call the view method in the initializer. For example:
- (id)init
{
if ((self = [super init])) {
_thing = 123;
_other = self.view.frame.size.width / 2;
}
return self;
}
viewDidLoad is called when the view loads. And the view loads as soon as you call the view method (or access the view property, if you prefer). So you should avoid referencing the view in init.
To answer your question, I prefer to create an init method for my view controllers.
- (id)initWithThing:(MyThing *)thing thang:(MyThang *)thang
{
if ((self = [super init])) {
_thing = [thing retain];
_thang = [thang retain];
// ...
}
return self;
}
You can also use properties to set extra variables after initing. Personally I prefer to do this with optional properties only, though, and put required properties in the init method. That way I can't init the view controller with an invalid state.
I encapsulate the state of my controllers in an additional state object. So the controller usually has ivars for the GUI elements and a reference to this state object.
State objects are handled by a StateManager object on my delegate. So instead having a controller referencing another controller and set variables directly, all changes go through this manager. A little more work but way less messy.
Any class is able to retrieve the state for any other controller and change it. This is the purpose of some controllers (eg: choosing a video from youtube happens on a dedicated controller). But usually is just one class getting a state to push the next controller with initWithState:. The pushed controller then applies the state to its GUI in viewDidLoad or changes this state object passed from the previous class.
The StateManager keeps a dictionary of all the state objects. I keep the state object graph light, eg: I store a reference to a image, but not the image itself. The real beef of the application is in Core Data, I only use all this to handle the GUI state. The StateManager listens for the application resign event and saves the state dictionary to disk using NSCoding. The state object ivars are always nil or some meaningful value, no dangling pointers.
I'm working on my 2nd iPhone app and this seems to be working, but I wonder too how other people does it. Any input is welcome.
Basically you should have properties defined for your input data but add a custom init-function.
If you create a custom init-Method you should be fine - remember that initWithNibName:bundle: is the main initializer of UIViewController so this is what you want to call from your custom init-Method. viewDidLoad will always be called after your init-Method at the first usage of customVC.view (either from your code or via Framework):
- (id)initWithDataObject:(MyDataObject*)obj
{
self = [super initWithNibName:nil bundle:nil];
if (self) {
self.dataObj = obj;
}
return self;
}
What is the recommended way of doing this. Should I call super dealloc first or last or doesn't it matter?
- (void)dealloc
{
[super dealloc];
[orderNumber release];
[orderDate release];
}
Also when it comes to overriding methods like didViewLoad - should I call super first or last?
Always call [super dealloc] last or you might easily come into trouble because you're working on a stale object.
With didViewLoad you normally call it before your own code as you want the standard initialization stuff executed before. I've seen examples in Apple's code that don't call the super implementation at all, though, so maybe there's not much going on anyway.
In this case call the super after you have released all your properties/iVars. For viewDidLoad/willAppear/etc. I usually call the super first. The order matters when your custom class is relying on an object that is created by the super. For the default viewDidLoad this is not the case so it is preference(I believe).
There is no general rule - you chose to override the method, what does it do? Do you want it to happen before or after your custom implementation?
didViewLoad doesn't appear to be a real method.
We know that [super dealloc] destroys the current object completely, so any code that comes after it is wrong. So, in this case, call [super dealloc] last.
The pointers orderNumber and orderDate are held inside your object.
[super dealloc] deallocates your object (aka self).
Once you deallocate your object you must not rely on the things inside it (e.g. orderNumber) having the values they did before you deallocated it.
Therefore, deallocate the members before deallocating the self object.
The opposite holds true for init functions - you can't initialise the pointers until after your object is constructed, so [super init] comes before you initialise the members.
Regarding viewDidLoad (et al), you do whatever works. If you have stuff you want to happen before the superclass does its thing, then you do it before you call the superclass method, and likewise for stuff you want to happen afterwards.
If you don't know whether your code should run before or after, then it probably doesn't matter.
-(void)viewWillAppear:(BOOL)animated{
//something here
[super viewWillAppear];
}
Is [super viewWillAppear]; always required? If not when and why do you use it?
First of all, the correct boiler plate should be:
-(void)viewWillAppear:(BOOL)animated{
[super viewWillAppear:animated];
//something here
}
In other words, call super first, then do your own thing. And you have to pass the animated parameter to super.
You usually want to call the super class' implementation first in any method. In many languages it's required. In Objective-C it's not, but you can easily run into trouble if you don't put it at the top of your method. (That said, I sometimes break this pattern.)
Is calling super's implementation required? In the case of this particular method you could get unexpected behavior if you don't call it (especially if you have subclassed a UINavigationController for example). So the answer is no not in a technical sense, but you should probably always do it.
However, in many other methods there may be good reasons for not calling super.
Calling super method provide possibility to execute code in parent class.
Regarding your question according to Apple doc
So, yes, this method required.
In my experience, calling [super viewWillAppear] in the first line, when calling reloadData after that, makes it impossible to retrieve the previously selected row when coming back from a detail view. When [super viewWillAppear] is the last sentence, you can get the selected row and show the previously selected row hint. This happens only when using [tableView reloadData] inside viewWillAppear.
Lets say you have 2 class, a Parent and a Child. Child inherits from Parent. They have a method called greet which returns a string.
Here is what the parent method looks like:
Code:
-(NSString *)greet {
return #"Hello";
}
We want the child to learn from his parents. So we use super to say greet how Mommy would greet, but with our own little additions too.
Code:
// Inherits from Parent
-(NSString *)greet {
NSString *parentGreeting = [super greet];
return [parentGreeting stringByAppendingString:#", Mommy"]
}
So now Parent greets "Hello", and the Child greets "Hello, Mommy". Later on, if we change the parent's greet to return just "Hi", then both classes will be affected and you will have "Hi" and "Hi, Mommy".
super is used to call a method as defined by a superclass. It is used to access methods that have been overriden by subclasses so that the class can wrap its own code around a method that it's parent class implements. It's very handy if you are doing any sort of inheritance at all.
I have an XML reader class which I initialize with a URL
- (id)initWithURL:(NSURL *)url
This class adds objects to an array in the calling class using an instance variable
// in the interface
ViewController *viewController;
// in the implementation
[viewController addObject:theObject];
Now, I initialize my XML reader class, then set the View Controller separately:
XMLController *xmlController = [[XMLController alloc]
initWithURL:url];
xmlController.viewController = self;
My question is whether I should create a new init function which sets the viewController at the same time.
Thanks.
Edit: I forgot to add that my XML reader starts downloading and parsing the class in the init function.
It's entirely up to you. You can see examples of both styles all over Apple's code.
As long as you don't make any assumption about the viewController property's value being constant over time, it should be fine to leave it as-is.
BTW, you might think about refactoring the addObject: logic into a protocol instead of requiring a specific subclass. Something like:
-xmlController:didDecodeObject:
Or whatever makes sense for your XMLController object's logic.
If your init routine is going to cause delegate/controller calls, or set off asyncronous activities (potentially including your downloading) that could message the delegate, then you should include it in the init function.
Otherwise your controller might miss potential delegate messages such as xmlController:didStartConnection that might be called before your initWithURL routine returns.
Also, if the controller/delegate is a required part of the XMLController activities, then you should include it in your init routine.
So yes, in this case I would suggest:
XMLController *xmlController = [[XMLController alloc] initWithURL:url andController:self];