How does initWithNibName work? - objective-c

I use to create my views programmatically and have started to switch using XIB files. I found this code:
-(id)init
{
self = [super initWithNibName:#"HelpViewController" bundle:nil];
if (self != nil) {
// further initialization needed
}
return self;
}
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
NSAssert(NO, #"Initialize with -init");
return nil;
}
It works but why? If I follow the logic, the initWithNibName returns nil and sets it to self. So, self is now nil and then you return self at the end of init. Well, that means you return self which is nil. Is that right?
Also, if I wanted to initialize a NSArray, where should I put it in that init function?
Thanks for the explanation.
Yko

Because the init method calls the self = [super initWithNibName...]. So You must call the init method to create the object. If you use initWithNibName it will fails
For Array you should initialize in init method
-(id)init
{
self = [super initWithNibName:#"HelpViewController" bundle:nil];
if (self != nil) {
// further initialization needed
myArray = [[NSMutableArray alloc] init];
}
return self;
}

You're looking at two different initWithNibName functions.
-(id)initWithNibName:(NSString *)nibNameOrNil bundle:(NSBundle *)nibBundleOrNil
{
NSAssert(NO, #"Initialize with -init");
return nil;
}
The above function is overriding the superclass version of initWithNibName. It raises an assertion informing the caller to use init.
self = [super initWithNibName:#"HelpViewController" bundle:nil];
The above line is calling the superclass version of initWithNibName, which returns a view controller.
If you wanted to initialize an array, you would initialize it where the "further initialization needed" comment is.

It works because you are calling -initWithNibName:bundle: on super (most likely UIViewController), rather than on self (your subclass of UIViewController). If you were to call initWithNibName:bundle on self, then you would hit the assertion or return nil if you have disabled assertions. The superclass implementation of -initWithNibName:bundle: is not affected by your implementation and therefore continues to behave as it normally would.

Related

Designated initializer should only invoke a designated initializer on 'super'

This code is a modified version of Ordered Dictionary implemented here.
https://github.com/nicklockwood/OrderedDictionary/tree/master/OrderedDictionary
Interface -> OrderedDictionary.h
#interface OrderedDictionary : NSMutableDictionary
{
}
Implementation -> OrderedDictionary.m
// This is the new method added.
- (instancetype)init
{
return [self initWithCapacity:0];
}
- (instancetype)initWithCapacity:(NSUInteger)capacity
{
self = [super init];
if (self != nil)
{
// Allocate here.
}
return self;
}
The code works fine but I get following warnings in "- (instancetype)init".
Designated initializer should only invoke a designated initializer
on 'super'
Designated initializer missing a 'super' call to a
designated initializer of the super class
What am I doing wrong and how do I fix it?
Made following changes to the code to fix the problem
// This is the new method added.
- (instancetype)init
{
self = [super init];
if (self != nil)
{
// Allocate here.
}
return self;
}
- (instancetype)initWithCapacity:(NSUInteger)capacity
{
self = [super initWithCapacity:capacity];
if (self != nil)
{
// Allocate here.
}
return self;
}
try adding
- (instancetype)initWithCapacity:(NSUInteger)capacity NS_DESIGNATED_INITIALIZER;
To the #interface block. All initialization paths should flow through the designated initializer - in your case initWithCapacity: is the obvious choice since init calls it. You may still run into problems, the designated initializer must call the super class's designated initializer. If the super class defines designated initializers those must be implement by your class as well...
In general subclassing NSMutableDictionary is considered bad practice because NSMutableDictionary is the public facing piece of a class cluster. This may be part of the cause of the compiler warnings.
As you can read in NSMutableDictionary documentation, there are two designated initializer for this class:
initWithCapacity: Designated Initializer
init Designated Initializer
Here you are calling from initWithCapacity in your class to super.init. That's the reason the compiler warns you.
This code maybe is better:
// This is the new method added.
- (instancetype)init
{
return [self initWithCapacity:0];
}
- (instancetype)initWithCapacity:(NSUInteger)capacity
{
self = [super initWithCapacity:capacity];
if (self != nil)
{
// Allocate here.
}
return self;
}
I'd bet some of you are tripping over this warning message when you have Xcode convert your code to "Modern Objective-C Syntax."
I've seen cases where Objective-C header files have been modified by the converter to have two designated initializers:
- (instancetype) init NS_DESIGNATED_INITIALIZER; // DO NOT USE
- (instancetype) initWithFileset: (NSArray *) fileset NS_DESIGNATED_INITIALIZER; // use this to instantiate
If you modify this code to a single initializer, you may find the warning message goes away:
- (instancetype) init; // DO NOT USE
- (instancetype) initWithFileset: (NSArray *) fileset NS_DESIGNATED_INITIALIZER; // use this to instantiate
In short, check the header and the source file when you see this warning message.
You should only call the super of your own function.
So replace
self = [super init];
with
self = [super initWithCapacity:capacity];

Init Not Returning Self

I have my default initialiser:
- (id)init
{
if ((self = [super init]))
{
}
return self;
}
I have only included this method in my class to track whether it was performing it's job, and from what I can tell, it is.
Self is getting set,and in the console I can see that self has a memory address which is not 0x0000000. For example, here is the console from a run I just tried:
self IssueManager * 0x08383ad0
However, when the method returns self to this method:
- (id)initWithNibName:(NSString *)nibNameOrNil
bundle:(NSBundle *)nibBundleOrNil
{
self = [super initWithNibName:nibNameOrNil bundle:nibBundleOrNil];
if (self)
self.issueManager = [[IssueManager alloc] init];
return self;
}
self.issueManager remains an empty pointer:
_issueManager IssueManager * 0x00000000
I have no idea why this is the case, but would appreciate any help.
Instead of using:
self.issueManager
try:
issueManager = [[IssueManager alloc] init];
Remember, using the dot notation is simply using the accessor/setter methods provided by #property and #synthesize. In your init method, you should not use accessor/setter methods.
I would not use an accessor in -init* (read this: Why shouldn't I use Objective C 2.0 accessors in init/dealloc?)
There is an Apple statement on this as well.
Try to do instead:
_issueManager = [[IssueManager alloc] init];

Designated initializer and initWithFrame

I've created a class with designated initializer defined that way:
-(id)initWithFrame:(CGRect)frame
aProperty:(float)value {
self = [super initWithFrame:frame];
if(self){
///....do something
}
}
Now I wonder how to avoid direct calls to initWithFrame method, like:
MyClass *theObj = [MyClass alloc]initWithFrame:theFrame];
I thought about throwing exception in initWithFrame definition:
- (id)initWithFrame:(CGRect)frame
{
#throw ([NSException exceptionWithName:#"InitError"
reason:#"This class needs to be initialized with initWithFrame:aProperty: method"
userInfo:nil]);
}
Is this a good solution ?
What about calling your designated initializer with a default value for aProperty ?
Two techniques I use with initializers. I'm sure there a more.
Technique 1:
In initWithFrame call your initializer with a default value for a Property
- (id)initWithFrame:(CGRect)frame
{
return [self initWithFrame:frame aProperty:1.0f];
}
Technique 2:
Simply return nil;
- (id)initWithFrame:(CGRect)frame
{
return nil;
}

Objective C init

Disclaimer, I'm new to Objective C. But I can't find this explained. I've seen two ways of implementing init:
- (id)init {
if ([super init]) {
return self;
} else {
return nil;
}
}
and
- (id)init {
if (self = [super init]) {
// do your init business here
}
return self;
}
so let's say i have:
myObj = [[MyObject alloc] init];
where MyObject class is a subclass of NSObject. in the second example, does init not return an initialized version of NSObject? so myObj would ... how would it know what it is? wouldn't it think it was an NSObject rather than a MyObject?
1) First version is just wrong. self should be always assigned with value returned by super initializer, because init<...> of super can return another object upon initialization (it's not unusual BTW). Second version is actually an 'official' way to implement init<...> methods.
2) 'wouldn't it think it was an NSObject rather than a MyObject'. myObj is instance of 'NSObject' and instance of 'MyObject'. It's the whole point of inheritance.
i just want to know, under the hood, how it does it.
It's pretty simple. 99.9% of all the classes you'll ever write will inherit from NSObject in some fashion. In the initializers, you're supposed to invoke super's designated initializer and assign it to self. Eventually, [super init] will be invoking -[NSObject init]. According to the documentation, that's implemented like this:
- (id)init {
return self;
}
So technically, if you inherit directly from NSObject, you're probably safe to not do the assignation of self = [super init];, because you know (and you're guaranteed) that this is equivalent to: self = self;, which is kind of pointless. Regardless, you should leave it in for consistency's sake.
However, once you start getting further down the inheritance chain, and especially when you're inheriting from opaque classes (ie, a class whose .m file you do not have), then things start getting shady. It is possible that you'll come across a class whose designated initializer looks something like this:
- (id) initWithFoo:(id)aFoo {
if ([aFoo isSuperFast]) {
[self release];
return [[SuperFastFooWrapper alloc] initWithFoo:aFoo];
}
self = [super init];
if (self) {
_foo = [aFoo retain];
}
}
This isn't as common, but it does happen. In this case, we're destroying self ([self release], to balance the alloc call that immediately preceded this) and instead returning a different object.

Should I always release self for failed init methods?

Should I always release self when there is a failure inside init, or should I only do so if I have initialized instance variables first?
To put it another way, is this pattern valid? Is there a time when I shouldn't release self inside an init method, or should I assume that if the control flow enters init, self has at least a retain count of 1?
- (id)init
{
if ((self = [super init]) == nil)
{
[self release];
return nil;
}
//do some init stuff
if (somethingFailed)
{
[self release];
return nil;
}
return self;
}
If some check you need in your initialization method fails, then yes you should release self. Note however, that if [super init] returns nil it does not make sense to send release to self as self is nil. This is actually frowned on by Apple:
You should only call [self release] at the point of failure. If you get nil back from an invocation of the superclass’s initializer, you should not also call release.
Example:
- (id)init
{
self = [super init];
if(self) {
// do some init stuff
if (somethingFailed)
{
[self release]
self = nil;
}
}
return self;
}
Also see the Mac Dev Center documentation on Handling Initialization Failure