Reputation: 7469
The Cocoa framework has a convention to always call self = [super init]
in the init
method of an inherited class, because [super init]
may return a new instance.
What will happen if I do this?
@interface MyClass : NSObject /* or any other class */ {
int ivar_;
}
@end
@implementation MyClass
- (id)init {
ivar_ = 12345;
if ((self = [super init])) {
NSLog(@"ivar_'s value is %d", ivar_);
}
return self;
}
@end
In the case when [super init]
returns a new instance, what will I see in the console? ivar_'s value is 0
?
I can't think of a way to check this myself, because I don't know which class may return a new instance from its init
method. Also, can't seem to find explicit clarification for this scenario in the docs.
Could anyone help me out? Thanks!
Upvotes: 5
Views: 1324
Reputation:
Under normal circumstances, when you send +alloc
to a class it returns a zeroed out instance of that class. It is already initialised in the sense that: a) it is a proper instance of that class, and b) all instance variables are zeroed out (numeric types are 0, C pointers are NULL, objects are nil, etc). However, no extra initialisation behaviour has been applied to the newly created instance — that’s the job of initialisers, -init
being the most common of them. In particular, if your class inherits from NSObject
then [super init]
doesn’t touch the newly created instance. So, in the code you’ve posted in your question,
+alloc
is sent to MyClass
, which returns a zeroed out instance. In particular, ivar_
is 0-init
is sent to the newly created instanceivar_ == 12345
because of the assignment in -init
, which could be rewritten as self->ivar_ = 12345
. At this moment, self
points to the instance returned by +alloc
[super init]
is sent, ivar_
isn’t touched since its superclass (NSObject
) doesn’t know about it, and the return value is assigned to self
. In fact, the return value is already self
so nothing’s changedself
is different from nil
, NSLog()
is called-init
returns self
Now let’s consider that [super init]
returns an instance that’s different from the one returned by +alloc
:
+alloc
is sent to MyClass
, which returns a zeroed out instance. In particular, ivar_
is 0-init
is sent to the newly created instanceivar_ == 12345
because of the assignment in -init
, which could be rewritten as self->ivar_ = 12345
. At this moment, self
points to the instance returned by +alloc
[super init]
is sent and it decides to release the current instance and return a different one. The return value is assigned to self
so, from the current perspective, -init
is now working on a different instance. The old instance was deallocated, hence that change to ivar_
is lost. ivar_
contains whatever [super init]
decides it should containself
is different from nil
, NSLog()
is called-init
returns self
, which is not the same instance created by +alloc
You can test this with the following code. Be warned that it’s just a quick example to illustrate my answer and it should not be used in production code.
#import <Foundation/Foundation.h>
@interface MySuperClass : NSObject
@end
@interface MyClass : MySuperClass
{
@public
int ivar_;
}
@end
@implementation MySuperClass
static MyClass *defaultInstance;
- (id)init
{
if ([self isMemberOfClass:[MyClass class]] && defaultInstance != nil)
{
[self release];
return defaultInstance;
}
return [super init];
}
@end
@implementation MyClass
- (id)init
{
ivar_ = 12345;
if ((self = [super init]))
NSLog(@"ivar_'s value is %d", ivar_);
return self;
}
@end
int main()
{
NSAutoreleasePool *pool = [NSAutoreleasePool new];
defaultInstance = nil;
defaultInstance = [[MyClass alloc] init];
NSLog(@"%d", defaultInstance->ivar_); // outputs 12345
defaultInstance->ivar_ = 98765;
NSLog(@"%d", defaultInstance->ivar_); // outputs 98765
MyClass *someInstance = [[MyClass alloc] init];
NSLog(@"%d", someInstance->ivar_); // outputs 98765
if (someInstance == defaultInstance)
NSLog(@"They're the same!");
[pool drain];
return 0;
}
The output is
ivar_'s value is 12345
12345
98765
ivar_'s value is 98765
98765
They're the same!
Upvotes: 5