Difference between initWithNibName and initWithCoder - iphone

Can anyone please explain me about when to use the initWithNibName and when to use initWithCoder?

initWithNibName: is usually used with a view controller object. The idea is that you have a NIB file (or XIB, same thing) that has a UIView (or NSView) you've already designed in Interface Builder. When your view controller launches, it has a view property and outlet that you would have to draw yourself -- except that you've already designed it in IB. So this constructor allows you to fire up the new controller object and tells it in which NIB file to look for its view. Discussion of wiring your NIB itself to make sure this is successful is a little beyond the topic here.
initWithCoder: has another task altogether. When you have serialized an object using encodeWithCoder:, you will eventually need to unserialize (or, "decode") that data to turn it back into an object of your class.
Anyway, to recap: You would implement encodeWithCoder: and initWithCoder: on your class only if you wanted your object to support the NSCoding protocol. You use initWithNibName: (typically you don't implement it yourself) when you want to fire up an object that can initialize its properties with objects archived in a NIB file.
There's a really great over-view of NSCoding over here.

Storyboard
You should prefer -initWithCoder: to -initWithNibName since only the former is invoked when loading a view from Storyboard.

From Apple's Documentation:
InitWithCoder encodes an object for archiving. A coder instructs the object to do so by invoking encodeWithCoder: or initWithCoder:. encodeWithCoder: instructs the object to encode its instance variables to the coder provided...
InitWithNibName Returns an NSNib object initialized to the nib file in the specified bundle. After the nib file has been loaded, the NSNib object uses the bundle’s resource map to locate additional resources referenced by the nib. If you specified nil for the bundle parameter, the NSNib object looks for those resources in the bundle associated with the class of the nib file’s owner instead. If the nib file does not have an owner, the NSNib object looks for additional resources in the application’s main bundle.
The former is used for encoding individual objects in your code, the latter is used to retrieve a NSNib file containing resource objects.

Related

How to pass references to init method of object in a nib?

I have a UIView subclass which currently assembles itself completely programatically. It has a custom initWithFrame:bundle: initializer which is necessary because it uses the bundle passed in to load image resources.
I want to make this view a subview in a larger nib file, but then initWithCoder will be called when the nib loading code gets to it instead of my custom initializer. Is there any way I can place this view in a nib and still have my custom initialization occur?
Yes, you can override initWithCoder: too if your bundle is known ahead of time (read: You don't need it passed in as a parameter). Otherwise, nope.
If you can wait until awakeFromNib, you can do your initialization in there. You still have the problem of not being able to pass the bundle into the method, though. Since awakeFromNib is called after initialization and setup of all outlets and actions, it might be too late for you. Maybe it's better to redesign around the nib-loading system anyway?

init is being called and I don't know why?

I am working on the iTuneU Stanford iPhone course HelloPoly drawing assignment, and I am getting a call to my object's init routine when I don't expect one. The callback seem to indicate that the call is coming from _loadMainNibFile (after other calls). What I am trying to understand is why is my object being init-ed implicitly. The source files can be found here: -- http://www.cavedrawings.com/hp2_files.zip
Can anyone tell me why the init routine would be called implicitly when loading the NIB file?
Most implementations of initWithCoder: ultimately call another initialization function. It's normal to stack initialization methods when you have a series of them that progressively add information to the initialization process.
_loadMainNibFile calls the initWithCoder: of the file owner of the nib which in turn calls another initialization method which leads up to the final init.
When a nib is loaded all objects within it get instantiated to do any prep work they need to do.
If you want a nib's object loaded and init'd later, put the object in a separate nib and explicitly load that nib when you need it.

was unable to load a nib named "TwitterDrilldownView"

-[UIViewController _loadViewFromNibNamed:bundle:] was unable to load a nib named "TwitterDrilldownView"
I get the above error when I push a new ViewController onto the navigation stack. This is the push code,
[self.navigationController pushViewController:[[[TwitterDrilldownViewController alloc] initWithTwitterAnnotation:temp] autorelease] animated:YES];
Basically I am just pushing a newly allocated and initialized view onto the stack. The init method of the ViewController is,
- (id)initWithTwitterAnnotation:(TwitterInfo *)aPOI {
if(self = [super init]) {
poi = aPOI;
}
return self;
}
As you can see I do not use any initialize with nib method and there is no nib file named TwitterDrilldownView in my project.
I did have a nib file before I created the TwitterDrilldownViewController called TwitterDrillDownView but I was using it to test a layout and, again, never used it. When I created TwitterDrilldownViewController the TwitterDrillDownView.nib was present in the project and it was after this stage that I deleted the nib.
The only cause for this problem that I can think of is that Xcode somehow created a dependency on the nib file because the nib file and view controller are named the same(TwitterDrilldownView.nib, TwitterDrilldownViewController.m), as if it was trying to be helpful but is ultimately messing up my project.
I have tried deleting and recreating the view controller in the hope that any references will be destroyed, and removed any reference to nib files in the project but to no avail.
Has anyone please got any experience with this problem or know a possible solution?
This also happened to me after deleting a XIB file out of my project. However, I did some messing around and was able to resolve the problem.
The key point is that Xcode seems to keep some kind of reference somewhere as you create XIBs and outlets/actions within them, and deleting a XIB file manually simply orphans these references. Removing these connections one-by-one, using the interface builder (UB) and the XIB seems to de-reference them properly.
The solution is then pretty clear:
Re-create a XIB file of the same name and similar construction (including the outlets and actions you had before). (Note: If you can't remember how to re-construct the XIB's outlets and actions, just re-create the XIB file with the same name and skip to my 'if it's still failing' note below.)
Manually, one-by-one, remove all of the outlets and actions in your XIB using interface builder (IB).
Re-build the app; if it works now, you can delete the XIB.
(Note: If it's still failing, you will probably have a different error. If the error refers to key-value compliance then it will list a method in the error - and that method is your problem. Make sure you recreate the named outlet/action in IB, and remove it manually using IB. That seems to de-reference the call in Xcode/your build environment.)
Happy hunting!
UIViewController should be initialized using initWithNibName:bundle: method. In its description stated:
This is the designated initializer for
this class.
If you specify nil for the nibName
parameter and do not override the
loadView method in your custom
subclass, the default view controller
behavior is to look for a nib file
whose name (without the .nib
extension) matches the name of your
view controller class. If it finds
one, the class name becomes the value
of the nibName property, which results
in the corresponding nib file being
associated with this view controller.
So if you do not load you view controller from nib file make sure you override loadView method and set controller's view property in it.
Hope that will help.

Automatically Loading XIB for UITableViewController

Ran into something interesting, want to know if I'm doing something wrong or if this is the correct behavior.
I have a custom UITableViewController. I ASSUMED (first mistake) that if you initialize as such:
[[CustomTableController alloc] init];
it would automatically load from a XIB of the same name, CustomTableController.xib, if it is in the same directory and such.
HOWEVER
This does not work; doesn't load the XIB. BUT, if I change the parent class of my controller from 'UITableViewController' to 'UIViewController', EVERYHTING WORKS FINE!
Calling:
[[CustomTableController alloc] init];
loads the controller and view from my xib.
Am I doing something wrong? Is this a bug? Expected behavior?
Most of the classes in Cocoa Touch list a "designated initializer" that you're supposed to call from your init methods when you subclass them. When you create your own custom class, it's a good idea to check the documentation to find the designated initializer for your superclass. When you initialize the class using some other initializer from a more general superclass (which you're doing by calling - [NSObject init] in this case), you rob your direct superclass of its opportunity to properly initialize its state. Sometimes you can get away with this. Often you can't.
UIViewController's documentation states that its designated initializer is -initWithNibName:bundle:. If you call this method with a nil nibName, it will look for a nib that matches your class name. The behavior of -init is undocumented for UIViewController. Based on the behavior you're seeing, it seems like it may be calling [self initWithNibName:nil bundle:nil], but it would be safer to call initWithNibName:bundle: directly rather than relying on this undocumented behavior.
UITableViewController only defines a single initializer, -initWithStyle: (although it doesn't specify this method as the designated initializer). This method initializes your UITableViewController without using a nib, which is usually fine. Since you don't add subviews to a UITableView, there usually isn't much to be gained by configuring your UITableViewController via a nib.
If decide you want to configure your UITableViewController via a nib anyway, the documentation tells us that we can safely bypass -initWithStyle: and call UIViewController's initWithNibName:bundle: method. Here is what the documentation tells us about how our UITableView and its controller will be initialized in each case:
If a nib file is specified via the initWithNibName:bundle: method (which is declared by the superclass UIViewController), UITableViewController loads the table view archived in the nib file. Otherwise, it creates an unconfigured UITableView object with the correct dimensions and autoresize mask. You can access this view through the tableView property.
If a nib file containing the table view is loaded, the data source and delegate become those objects defined in the nib file (if any). If no nib file is specified or if the nib file defines no data source or delegate, UITableViewController sets the data source and the delegate of the table view to self.
In summary, the documentation for most Cocoa Touch classes either specify a single designated initializer or a handful of initializers that you can call safely from your subclasses. Always refer to the documentation for your superclass to figure out which initializer your subclass should call.

iPhone programming, how objects work?

Another noob question, but while looking at some code examples, I see stuff like
UIViewController *controller = [[UIViewController alloc] init...
Making an instance of the class UIViewController which I understand. But then you have these instance variables of your own class. Like let's say I was building a simple counter program called Counter and it counts when you touch the screen and I have a property for my view controller called "count." So when I run my program, is that when my instance of my class is created? Because it's not like I ever create a Counter *counter object. Thanks.
So when I run my program, is that when
my instance of my class is created?
Yes, when your program executes the objects are created.
If you have allocated and initiated counter but are using it, I'm surprised anything works.
You allocate and initialize objects before you use them and then release them when you are done.
I think you may be confused because of the way loading nib (XIB) files instantiates objects without any actual Objective-C code to instantiate the object.
Read about the NIB Object Lifecycle in this guide: https://developer.apple.com/library/archive/documentation/Cocoa/Conceptual/LoadingResources/CocoaNibs/CocoaNibs.html#//apple_ref/doc/uid/10000051i-CH4-SW18
Basically, the first NIB gets loaded based on whatever is specified in the YourAppName-Info.plist for "Main nib file base name" and that will instantiate any objects defined in there and then set IBOutlets to point to them as specified in the NIB file. As a result, certain objects will seem to just "be there" referenced in the outlets (like IBOutlet UIWindow *window in your app delegate) by the time your code executes.
I recommend the book Beginning iPhone 3 Development as a great tutorial for all of these things.