I've found a case where some of my view controllers' initWithCoder methods are invoked before the application didFinishLaunching method in the application delegate. (I've confirmed this by setting breakpoints and looking at the sequence of invocations)
I'm using a storyboard. A UITabBarController is the initial view controller. Part of the problem is that the storyboard creates objects in an unknown order; perhaps it's creating the view controllers before the app is done launching.
In any case, the problem is that I'm registering initial user defaults. This must happen before any piece of the program looks at them. So, I'm trying to find the spot where the registering code will be guaranteed to execute first.
Is there any such place?
Note:
This thread discusses it a little, but there isn't really a conclusion...
ViewDidLoad runs before AppDelegate didFinishLaunchingWithOptions gets executed!
The standard means of initializing user defaults is in a "+(void)initialize" method in your app delegate:
+ (void)initialize
{
if(self == [MyAppDelegate class]) {
...
}
}
This is guaranteed to run before any delegate method gets messaged.
PS: I instantiate a whole bunch of viewControllers in my didLaunch method before returning from that method.
This is natural (and also one of the reasons why using InterfaceBuilder sucks). In application:didFinishLaunchingWithOptions: you generally rely upon the main window and main view controller having already been created from their corresponding NIB/XIB files. Two solutions:
One (preferred): instantiate stuff manually in application:didFinishLaunchingWithOptions:. You can thus control the execution order of any initialization.
Two: use __attribute__((constructor(XXX))) functions - they're guaranteed to be called before main, and the lower the XXX number the earlier the particular constructor function is called. This method is, however, not advisable, because it isn't standard C (only a compiler extension), and it also can easily get very confusing.
Related
I am using a UITabBarController, and my 3rd tab observes an array on a singleton data store (implemented in viewDidLoad).
Currently if I just log out (and change root view controller from App Delegate), the app will crash when dealloc is called on that 3rd tab with the message "cannot remove observer for the key path "X" because it is not registered as an observer.
Using breakpoints, I see that viewDidLoad is never called on this 3rd tab, however dealloc is being called when I sign out. What is going on? I assume the UITabBarController is holding a reference to the 3rd tab when I enter the storyboard, but does not "load" that tab. Yet iOS calls dealloc on it when I release the tab bar controller.
Should I use a boolean to track viewDidLoad execution, or try to remove the observer with a #try statement? Is there an overall better design for this?
Do not use #try. Exceptions in Objective-C should always be considered programmer error, and should be fatal.
As you say, use a boolean ivar set in -viewDidLoad to avoid this.
The view has not been loaded because views are only loaded when they are required for display.
Raw KVO can be dangerous and unwieldy. While not required to answer this question, ReactiveCocoa significantly improves the KVO experience.
viewDidLoad is called before the view appears for the first time. UITabBarController is creating the relevant UIViewController, but the view is not loaded during creation. It is loaded on-demand, when a user visits the tab for the first time.
KVO removal is problematic, I don't think you can avoid using #try in dealloc. I would suggest to use KVOController: it's fairly easy to use and it would also handle all the edge cases for you.
May have found an even better solution. I add the observer in the method initWithCoder:(NSCoder *)aDecoder, which is called when the parent UITabController is loaded. I am using the storyboard which may be why I need to call override this method instead of regular init. Doing this now without the need for a BOOL flag or #try and no crashing.
- (instancetype)initWithCoder:(NSCoder *)aDecoder {
if (self = [super initWithCoder:aDecoder]) {
[anObject addObserver:self forKeyPath:aKeyPath options:0 context:NULL];
}
return self;
}
Use a flag to set whether or not KVO has been set up. Using #try can create memory management issues depending on the state of the app.
I placed my code for iAd/AdMob ads in...
-(void)viewWillAppear:(BOOL)animated{}
Ads work perfectly fine the way I have them now on all iOS devices.
When I connected my iPhone to Xcode and clicked on Product -->Analyze a message states...
The viewWillAppear:instance method in UIViewController subclass 'iPhoneSIX' is missing a [super viewWillAppear:] call
I just accidentally stumbled upon this Product-->Analyze thing. Do I really need to add [super viewWillAppear] even though everything works perfectly fine on all devices as it currently is. Will Apple reject my app if I don't pay attention to the Product-->Analyze issue navigator?
Also, what does ...
[super viewWillAppear:YES];
What does calling this do?
According to Apple: (emphasis mine)
This method is called before the receiver's view is about to be
added to a view hierarchy and before any animations are configured for
showing the view. You can override this method to perform custom tasks
associated with displaying the view. For example, you might use this
method to change the orientation or style of the status bar to
coordinate with the orientation or style of the view being presented.
If you override this method, you must call super at some point in your
implementation.
Apple doesn't gets that specific when deciding to Accept or Reject your app. It only follows the guidelines, which doesn't get that much into the weeds of your specific methods.
Calling [super viewWillAppear:YES] is a best practice, and I would recommend it. Always including super ensures that any code in the super classes get called before executing any additional code. So if you or someone else coded a super class that expected some code to be executed, you are guaranteed to still execute it, rather than just overwriting the whole method in the subclass.
Say you have a view controller of type MyViewController which is a subclass of UIViewController. Then say you have another view controller of type MyOtherViewController, which is a subclass of MyViewController. Say you're coding now some things in viewWillAppear in MyOtherViewController. If you call super first, it will call viewWillAppear in MyViewController before executing any code. If viewWillAppear in MyViewController calls super first, then it will call viewWillAppear in UIViewController before executing any code.
I'm quite certain Apple will not reject your app for failing to call super on an overridden method, primarily because there are cases where you may specifically want to avoid calling super.
That said, as Josh Gafni mentions it is definitely a best practice to do so, unless you have a very good reason for not. Also bear in mind some view controller subclasses (can't recall specifically which ones, but maybe UICollectionViewController) will only work properly if their view lifecycle methods get called appropriately, so not calling super can definitely break some classes (sometimes in subtle ways you may not realize).
Therefore my suggestion is add the call to super (generally as the first line in the method) and see if things continue to work fine. If not, spend a bit of time trying to understand what is happening differently and see if you can solve it in a different way. In general you should always (as a force of habit) provide calls to super on any view lifecycle methods you override whenever possible.
For implementing unwind segue we need make control-drag in the storyboard and
create private method in the destination view controller.
I don't understand : if method is private, how it works? Because in the implementation file of my view controller this NOT calling.
When executed, your application parses the .storyboard file at first, getting to know what classes it needs to instantiate and other important information, like segue connections. Then, it creates class instances (and manages them later). Then, when requested (e.g. after a button press), it attempts to find a method unwindToList: in the controller instance. If there is one, it would execute. If not, an exception would be thrown.
However, that method is not exactly hidden. Basically, there is no such thing as a private method in Objective-C; at least not in the same way as some other languages have. Every method is accessible by the runtime. If you're interested in how Objective-C works exactly, take a look at these pages, for example:
How does objective-c handle method resolution at run-time?
http://cocoasamurai.blogspot.ru/2010/01/understanding-objective-c-runtime.html
I am converting an iPhone app to a Universal app. I have a NIB view which I want to use on the iPad as is but resized and positioned. On the iPhone I am initializing normally with initWithNibName...
EventEditViewController *eventEditViewController = [[EventEditViewController alloc] initWithNibName:#"EventEditViewController" bundle:nil];
I found that this did not work well for me on the iPad for various reasons. So I created my own initialization method to call instead when running on the iPad...
EventEditViewController *eventEditViewControllerForIPad = [[EventEditViewController alloc] initWithFrame:iPadFrame eventDate:longDate event:eventName delegate:self];
This solved a couple of problems. One how to resize and position the view where I wanted it and how to properly initialize certain variables. I am actually passing more variables than you see here.
It works really well, but I just now noticed that, unlike initWithNibName viewDidLoad fires before my initWithFrame method. I only found this out because a variable I was trying to access in viewDidLoad was showing up as a zombie and I thought I was initializing it in my initWithFrame method.
I was surprised by this behavior. Is it normal? It doesn't make sense to me that the view would be loaded before the named initMethod in the alloc/init call.
I am now wondering if what I am doing might not be a good thing. Like I said it works really well, but should I not use my own initialization method here?
If it's ok to do it this way, maybe someone can explain why the view loads before the init method.
Thanks,
John
If your -initWithFrame:... method is accessing the view controller's view property, -viewDidLoad will be called before the init method completes because the view accessor will cause the view to be loaded.
As for whether it's okay to use your own method, it should be fine provided that your init method calls the designated initializer for the class.
Initializing member variables should be done in viewDidLoad or awakeFromNib.
awakeFromNib is the first method that gets called when a view comes to life from a Xib.
It's preferred to use viewDidLoad for allocating memory for huge arrays since you can deallocate them in viewDidUnload.
Both navigation controller and tab bar controller uses view loading methods to unload views when other views demand more memory.
Allocating in anyother methods should be avoided as far as possible.
From Apple's PageControl source code
// load the view nib and initialize the pageNumber ivar
- (id)initWithPageNumber:(int)page
{
if (self = [super initWithNibName:#"MyView" bundle:nil])
{
pageNumber = page;
}
return self;
}
You can have your own custom init method defined in that EventEditViewController and you can use a custom method like above to initialize your viewController and set as many member variables as you want like iPadFrame, longDate, eventName etc in your case.
Just make sure you call it exactly as above as it's important to call super implementation in such custom init methods.
Also just to shed more light on where you should release arrays you created in viewDidLoad method, it's the dealloc method first in addition to viewDidUnload. The reason behind this is viewDidUnload method doesn't always get called. It gets called only when application starts receiving memory warnings. As compared to this, the method dealloc gets automatically called always when you release the initialized viewController and it's retain count reaches 0. You should release the arrays you initialized viewDidLoad method and your other retain properties in dealloc method.
Also keep in mind that when the app receives memory warning, it's actually a chance to free up additional memory. Also the viewDidUnload method gets called for all the viewControllers in memory except visible one at that time.
I think to know what the App Delegate does. It has some nice methods like -applicationDidFinishLaunching which will be called when the app has finished launching, and so on. But what's that actually? Is that some object instantiated in the UIApplicationMain function?
And how does it work that every class in my app has access to that App Delegate object? Is there any good graph on the net that visualizes these relationships?
In Cocoa, a delegate is an object that another object defers to on questions of behavior and informs about changes in its state. For instance, a UITableViewDelegate is responsible for answering questions about how the UITableView should behave when selections are made or rows are reordered. It is the object that the UITableView asks when it wants to know how high a particular row should be. In the Model-View-Controller paradigm, delegates are Controllers, and many delegates' names end in "Controller."
At risk of stating the obvious, the UIApplicationDelegate is the delegate for the UIApplication. The relationship is a bit more obvious in Cocoa (Mac) than in Cocoa Touch (iPhone), since the NSApplication delegate is able to control the NSApplication's behavior more directly (preventing the application from terminating for instance). iPhone doesn't permit much control over the UIApplication, so mostly the UIApplicationDelegate is informed of changes rather than having an active decision-making process.
The UIApplicationDelegate isn't strictly available from everywhere in the app. The singleton UIApplication is ([UIApplication sharedApplication]), and through it you can find its delegate. But this does not mean that it's appropriate for every object in an app to talk directly to the app delegate. In general, I discourage developers from having random objects talk to the app delegate. Most problems that are solved that way are better solved through Singletons, NSNotification, or other delegate objects.
As to its creation, on Mac there is nothing magical about the app delegate. It's just an object instantiated and wired by the NIB in most cases. On iPhone, however, the app delegate can be slightly magical if instantiated by UIApplicationMain(). The fourth parameter is an NSString indicating the app delegate's class, and UIApplicationMain() will create one and set it as the delegate of the sharedApplication. This allows you to set the delegate without a NIB (something very difficult on Mac). If the fourth parameter to UIApplicationMain() is nil (as it is in the Apple templates), then the delegate is created and wired by the main NIB, just like the main window.
The object is instantiated in this fashion;
The main-function looks for the main nib set in the info.plist. The nib has as app delegate which is set to some class which must implement UIApplicationDelegates and its required methods. The app delegate then loads some viewcontroller.
It serves as an application wide callback object for events that affects the whole application, such as low-memory, etc.