Is it safe to reinitialise self within a class method?
MyClass * a = [[MyClass alloc]init];
#implementation MyClass
{
-(id)init
{
if(self = [super init])
{
...
}
return self;
}
-(void)redefine
{
//??
self = [self init];
}
}
will a point to the reinitialized instance of MyClass?
Thank You,
nonono
Provided that (a) your class and its superclasses can be re-init'ed without leaking memory or resources and (b) you know that your class and its superclasses inits all return the self they are passed and not a reference to some other object, then yes...
Otherwise things will go wrong. Consider your redefine method; in the body of this method self is just a local variable whose contents is initialized to point to some object. Changing the value in that local variable does not change the object it originally pointed at, or the value of any other variables which point to that object. E.g. consider the variation:
#implementation Q
{
- (void) redefine
{
self = [[Q alloc] init]; // changes the *local* self to refer to a new object
}
...
}
...
Q *someQ = [[Q alloc] init]; // allocate an object
[someQ redefine]; // NO effect on someQ, another Q is just created and leaked
Clearly this does not alter someQ, and your version may not either. Your code will have the effect you wish if and only if you know init always returns the object it was passed - which is not guaranteed in Obj-C.
As long as init returns self, which it normally does, nothing will go wrong.
But you probably want to split your initialization to some separate method, which you can call from both init and redefine.
You need to return your new object from -init, not simply assign a new value to self. And you must remember to release the old self, since it was created with +alloc. Caveats aside though, returning a different object from -init is explicitly allowed. That's why you'll see newbies being corrected when they write something like this:
// Bad example! Do NOT do this!
Foo *foo = [Foo alloc];
[foo init];
This is an anti-pattern because -init is not required to return the same object it was called on. That means the above can end up assigning foo to point to an object that's been released, instead of to the object that was initialized in its place. This is why you always see +alloc and `init chained together like so:
Foo *foo = [[Foo alloc] init];
It's also why you need to reassign self when calling super's -init, because it may also have returned a different object.
self = [super init];
Related
Forgiveness, please: I am a beginner. I was looking at another quesiton/answer and came across this code:
SpinningView *spinner = [[SpinningView alloc] initWithFrame:CGRectMake(0.0, 0.0, 20.0, 20.0)]
// Now let's take a look at the implementation of SpinningView's -initWithFrame: method
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self)
{
self.backgroundColor = [UIColor clearColor];
}
return self;
}
I believe that, in the second section of code, self points to the instance to which the message was sent that resulted in "self" being encountered, i.e., the result of [SpinningView alloc]. (Or doesn't that produce an instance?)
So, when you call self = [super initWithFrame:frame] on the 4th line of code, are you not reassigning the pointer value associated with "spinner"? I.e, are you not abandoning the memory you allocated in the first line? Or does the compiler someone know just to copy memory values instead of changing the pointer value? Or... what??
Thanks!
This is the standard idiom for the -init method of obj-c objects. The idea being that, whatever was allocated from +alloc doesn't matter, only what was returned from -init matters. Now, -init will usually just use the already-allocated object that's in self. But it's not required to. It is free to deallocate that object and create a new one. The classic example is when you alloc/init an NSString* you don't actually get back an instance of NSString*, you get back a concrete subclass. This is because NSString* is a "class cluster". So when you call +alloc you get back an NSString*, but when you call -init it frees that object and reallocates an object of one of its subclasses, initializes that new object, and hands it back to you.
Another example would be if you had a class that tried to memoize itself. Lets say you have an immutable class that gets initialized with a number. You could change your -init to re-use existing instances of the class. Here's an example (note: not thread-safe):
static NSDictionary *numberCache;
#interface MyNumber : NSObject
#property (readonly) int number;
- (id)initWithInt:(int)i;
#end
#implementation MyNumber
+ (void)initialize {
if (self == [MyNumber class]) {
numberCache = [[NSDictionary alloc] init];
}
}
- (id)initWithInt:(int)i {
// find ourself in the numberCache
NSValue *val = [numberCache objectForKey:#(i)];
if (val) {
// yep, we exist. Release the just-allocated object
[self release];
// and retain the memoized object and stuff it back in self
self = [[val nonretainedObjectValue] retain];
} else if ((self = [super init])) {
// nope, doesn't exist yet. Initialize ourself
_number = i;
// and stuff us into the cache
val = [NSValue valueWithNonretainedObject:self];
[numberCache setObject:val forKey:#(i)];
}
return self;
}
- (void)dealloc {
// remove us from the cache
[numberCache removeObjectForKey:#(_number)];
[super dealloc];
}
#end
#KevinBallard covered most of the points. The reason we need the self = is because init is not guaranteed to return the same object it is called on (it could return a different object or nil). I will answer your questions and expand on the memory management aspects:
I believe that, in the second section of code, self points to the
instance to which the message was sent that resulted in "self" being
encountered, i.e., the result of [SpinningView alloc].
Yes
So, when you call self = [super initWithFrame:frame] on the 4th line
of code, are you not reassigning the pointer value associated with
"spinner"?
Yes. Not spinner (spinner doesn't exist at this point anyway). You are re-assigning the pointer variableself inside the method.
I.e, are you not abandoning the memory you allocated in the first
line? Or does the compiler someone know just to copy memory values
instead of changing the pointer value? Or... what??
Yes. Under MRC, you are just re-assigning the pointer, and the compiler does not do anything except change the pointer value. Under ARC, it's more complicated, but at the end of the day, the compiler just does the same as under MRC in this case, i.e. just re-assigns the pointer.
It's not really "abandoning" the memory if you think about it. You see, by convention, init methods take ownership of ("consume") an already-retained object that they're called on (usually the return result of a call to alloc), and they return a retained object. But these two don't have to be the same object. So when your init method is called, its self is already retained, and the init method owns it, but then it calls [super init...], which calls the superclass's init method on self, so that method now takes ownership of the self which your init had ownership to. And in return, that superclass's init returns back to you a retained instance, which you assign to self. You did not "abandon" self because you gave it to the superclass's init method, which in turn became responsible for memory managing it (including releasing it if it wants to return something else).
I come from a Java background and I am learning Objective C. I am trying to create a class that has a string array and a member function to modify the Array. My code looked like this:
#implementation TAWChapter
#synthesize mSubject;
#synthesize mItems;
- (id) init{
self.mItems = [[NSMutableArray alloc] init];
return self;
}
- (void) setSubject:(NSString *)subject{
self.mSubject = subject;
}
- (void) addItem:(NSString *)item{
[self.mItems addObject:#"asdf"];
}
#end
Which didn't work. I got a "[__NSArrayI addObject:]: unrecognized selector sent to instance " and a "NSInvalidArgumentException". After searching on internet, I changed the single line in the constructor to:
self.mItems = [self.mItems init];
It worked, but why? From a Java developer's point of view the first one makes more sense than the second one. And I have another line it's the same as the first one but it's working(not in a constructor). Can someone explain this to me please?
First of all, you should adhere to Objective-C coding conventions. In Objective-C, classes don't have constructors, they have initialisers. In Objective-C, initialisers invoke the initialiser of the superclass, so it should look like this:
- init
{
self = [super init];
if (!self) return nil;
// set up other stuff here
return self;
}
Second, unless you are using ARC, you might have a memory leak. The first line of your initialiser assigns an object that you own to a property that also likely takes ownership. You should use either:
// property takes care of ownership
self.mItems = [NSMutableArray array];
or:
// assign to instance variable directly with owned object
mItems = [[NSMutableArray alloc] init];
Apple sometimes discourage the use of accessor methods in initialisers because it can fiddle with things like KVO, but consistent use of accessor methods ensures proper object ownership and memory management.
By changing your line in your initialiser to:
self.mItems = [self.mItems init];
does nothing. When your initialiser method is called (which is typically just after it has been allocated), all instance variables are automatically set to nil. So what you are doing is just:
self.mItems = [nil init];
which is just:
self.mItems = nil;
and, don't use init without first allocating an instance, and never use init more than once.
If you do not let the superclass initialise itself, then it may manifest as problems in other areas. Do a Build & Analyze and ensure you have fixed up any issues pointed out by the analyser.
Since objective-c is a superset of c, it's basically c with some "OO" syntax sugar. Before you can create (or use!) an object, you must alloc space for it in the heap. you do this with [Class alloc]. The next step is the initialization of that space. alloc returns a pointer to that space in the heap, which you initialize with init ;)
So you call Class *myObjc = [[Class alloc] init];.
If you use inheritance (which you do since you inherit from NSOBject), you must make sure that your superclass initialized everything properly, thats the call to super. To make sure you don't get a runtime error, check for self != nil, which you do implicitly with if(self)
self.mItems = [self.mItems init]; // doesn't do anything, since you call the getter for mItems with self.mItems and try to init. then you try to set mItmes to itself.
use this code:
#implementation TAWChapter
#synthesize mSubject, mItems;
- (id)init
{
self = [super init];
if (self) {
mItems = [[NSMutableArray alloc] init];
}
return self;
}
- (void) setSubject:(NSString *)subject{
mSubject = subject;
}
- (void) addItem:(NSString *)item{
[mItems addObject:item];
}
#end
You should call super and assign its result to self in your init method:
- (id)init
{
self = [super init];
if (self) {
self.mItems = [[NSMutableArray alloc] init];
}
return self;
}
The another way could be creating NSMutableArray from NSArray:
NSMutableArray *myMutableArray = [NSMutableArray arrayWithArray:myArray];
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
why does initializing subclasses require calling the super class's same init function?
I really can't understand the role of super in initializing an object.
For example, having this (example — not written by me) code:
#implementation MyObject
- (id) init
{
if([super init]){
return self;
} else {
return nil;
}
}
#end
What [super init] actually does? I'm confused, can't get the point
It is necessary to ensure correct initialisation of inherited instance variables from the super class of MyObject.
Since Objective-C is object oriented you can inherit from other classes. When you inherit from other classes you can intercept messages and get to decide if you pass them on to the class you inherit from. In the case of init it is almost always important to do self = [super init] or use the designated init method of the class to make sure the object is created correctly. Imagine if in MyObject in your init method you create an NSMutableArray that your class uses but init was never called because someone else inherited from your class and never called [super init]. You would then have nil references or either a bad pointer every where you attempted to used your NSMutableArray. The reason why it is important to set self equal to [super init] is the value of self may change such as in error recovery.
//this is valid
-(id)init
{
if((self = [super init]))
{
if(someInitializationFails)
{
[self release];
self = nil;
}
}
return self;
}
Wil Shipley recommends this (from 2009):
- (id)init;
{
if (!(self = [super init]))
return nil;
// other stuff
return self;
}
But why assign super init's return to self?
Matt Gallagher's article attempts to explain it...
-- Quote:
If you remember back at the start, I
said that the initWithString: part of
a typical [[MyClass alloc]
initWithString:#"someString"]
invocation is converted into an
objc_msgSend call:
MyClass *myObject2 = objc_msgSend(myObject1, initSelector, #"someString");
So by the time we get
to the inside of the method, self
already has a value; its value is
myObject1 (i.e. the allocated object,
as returned from the [MyClass alloc]
call. This is essential because
without it, the super invocation
wouldn't be possible — the self value
is used by the compiler to send the
invocation:
[super init];
becomes:
objc_msgSendSuper(self, #selector(init));
Yes, self already
has a value when your initializer
starts. In fact, it is almost
guaranteed to be the correct, final
value.
-- Unquote
Essentially, I think a lot of people are left confused as to what each init method's 'self' is pointing to exactly, up through the superclass chain.
The answer to this riddle is implied in Apple's Objective-C Programming Language doc, under the section titled Designated Initializers:
Note that B version of init sends a
message to self to invoke the
initWithName: method. Therefore, when
the receiver is an instance of the B
class, it invokes the B version of
initWithName:, and when the receiver
is an instance of the C class, it
invokes the C version.
Or, in other words, the 'self' variable points to the our instance that is being initialized. Again to reemphasize, all of these init methods up through the superclass chain are inherited by our instance, and as such, the 'self' variable in them points to our instance (unless explicitly changed) .
Am I right? Of course!
I have read many posts about this now but I do not still understand it. I would appriciate an answer rather than a link because I probably already have read it.
if (self = [super init]) {
}
return self;
When I am calling the [super init] I know I am calling the method on "self"(the objects address) but I am starting the "method-search" in the superclass. When this returns I assign the object type id to self...This is where I am getting lost.
Am I assigning "self" as an initialized object up to the point of the superclass to self..?
I understand that I am doing this check to stop the initializing if the superclass implementation of the initializer returns nil however I dont understand what I am assinging to self....I thought self was an address to the current object in memory.
Thanks in advance
The assignment has always seemed a bit hacky to me. Its main point is that the superclass might want to return some other instance than the one that was initially allocated:
id foo = [[Foo alloc] init];
#interface Foo : SuperFoo {…}
#implementation Foo
- (id) init
{
self = [super init];
if (!self)
…;
return self;
}
#interface SuperFoo : NSObject {…}
#implementation SuperFoo
- (id) init
{
[self release];
return [OtherClass alloc];
}
This is crazy indeed, but the fact is that [super init] might return an object different from the previous self. See Mike Ash’s blog post, that should make things super clear.
There are two reasons, why that assignment is important:
The designated initializer (di) of the superclass may return nil if initialization fails.
In this case, without the assignment of its return value to self, you would end up in a state that is completely unsafe — most likely, your superclass's di will have released the object pointed at by self in order to not leak memory.
If you went on using that instance and you're lucky you should see a crash in the not so distant future. If you're not that lucky, you're going to mess with some other object's internal state and lose or corrupt user-data before your program crashes.
There are quite a few classes in Cocoa(Touch) — the class-clusters like NSString and NSArray probably being the most prominent examples — that may return a different instance from their di.
The pointer you will receive from [NSString alloc] for example will almost definitely not be the same you'll obtain from a subsequent call to initWithFormat:#"Hello %#!", #"foo".
lets break this into smaller chunks:
1- when your calling [super init] your making your super class run its init function first so it can initialize your object that your inheriting, normally that would be NSObject or any superclass that you decided to extend.
the super init functions will return self at the end of that process, just like your doing in your init function
2- when you do the assignment: self = [super init] your actually assigning that return value from your super into your own.
3- the if around that assignments actually evaluates the success/failure of the super init call, cause if it failed you would have got a nil back and the assignments would have been nil to self. so evaluating nil will return false and you wont run your init code.
4- eventually you also return self (nil if failed / actuall object if it succeeded)
hope that clears it.
Can someone explain to me what init and alloc do in Obj-C. I am reading this obj-c book that gives an example of creating object but it does not really go into details of what it does. What does alloc return? what does init return?
Animal * k = [Animal alloc];
k = [k init];
alloc allocates a chunk of memory to hold the object, and returns the pointer.
MyClass* myObj = [MyClass alloc];
myObj cannot be used yet, because its internal state is not correctly setup. So, don't write a code like this.
init sets up the initial condition of the object and returns it. Note that what's returned by [a init] might be different from a. That explains the code Yannick wrote:
-init{
self=[super init]; // 1.
if(self){ // 2.
....
}
return self; // 3.
}
First, you need to call the superclass's init, to setup the superclass's instance variables, etc. That might return something not equal to the original self, so you need to assign what's returned to self.
If self is non-nil, it means the part controlled by the superclass is correctly initialized. Now you perform your initialization. All of the instance variables are set to nil (if it's object) and 0 if it's integer. You'll need to perform additional initial settings.
Return the set-up self. The returned self might be different from what's allocated! So, you need to assign the result of init to your variable.
This suggestions an important lesson: never split the call to alloc and init. Don't write:
MyClass* myObj = [MyClass alloc];
[myObj init];
because [myObj init] might return something else. Don't try to get around this by writing:
MyClass* myObj = [MyClass alloc];
myObj=[myObj init];
because you will eventually forget to write the part myObj= in the second line.
Always write:
MyClass* myObj = [[MyClass alloc] init];
I also don't recommend writing:
MyClass* myObj = [MyClass new];
because it does not correctly call the initialization method: some classes doesn't accept a plain init. For example, NSView needs initWithFrame:, which can't be called with new. So, don't use new either.
In its simplest form:
alloc: short for allocation, reservers a memory location and returns the pointer to that memory location. This pointer is then stored in the k variable.
init: short for initialization, sets up the object and returns the object. The init method depends on the object, but it generally involves sending the init message to the superclass. And if that init method (of the superclass) returns an object (not nil) some ivars may be set up depending on the task of that class.
--
Example of an init implementation, the Schedule class initializes its channels ivar with an empty array. Basically your giving the Schedule object a chance to sort itself out, so it can start receiving messages once it is created. You could remove the channels initialization from the init method, but then you would have to check if the channels ivar is nil in every method of the Schedule class and initialize it if it is indeed nil.
- (Schedule*)init {
self = [super init];
if (self) {
channels = [[NSMutableArray alloc] initWithCapacity:0];
}
return self;
}
alloc and init are two methods that are inherited from NSObject
you can provide your own methods or call the ones from NSObject
alloc allocates the memory to create a new instance of your class(#interface)
init initializes the contents of that instance, by default init sets all member values to 0/nil however you can create your own init method to customize what is done.
Animal * k = [[Animal alloc] init]; // creates a new Animal object
you can also write
Animal * k = [Animal new]; // which would be a bit more similar to other languages
The NSObject root class provides a class method, alloc,
alloc: reservers a memory location and returns the pointer to that memory location.
The alloc method has one other important task, which is to clear out the memory allocated for the object’s properties by setting them to zero.
This avoids the usual problem of memory containing garbage from whatever was stored before, but is
not enough to initialize an object completely.
You need to combine a call to alloc with a call to init, another NSObject method:
(id)init;
The init method is used by a class to make sure its properties have suitable initial values at creation.
The correct way to allocate and initialize an object is to nest the alloc call inside the call to init, like this:
ClassName *objectName = [ClassName alloc] init];
from: apple documents
Alloc will allocate the memory for the object, but Init puts the object into the memory and sets the default values.