Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

using alloc and init

We know about the complete pattern of alloc/init that alloc and init must be combined.

NSObject *myObj = [[NSObject alloc] init];

1- init method receives the object from another source(not from a alloc,new,copy or alike or retained) so according to the fundamental memory management rule its not the owner and it must not release it. However, "Allocating and Initializing Objects / The Returned Object" article says that init could free the receiver.

How could this be possible when its against the fundamental rule?

2- Also, from the same article, init could return another object or nil. So, in this case, when we use the complete pattern of alloc/init, we could not release the object returned by alloc but we could only release the object returned from init and, init releases the object it received from alloc instead of us.

But init is not a alloc,new,copy or alike method so we must not release the object returned from it as it does not give us the ownership of object.

How could we release the object returned from init although this is against the fundamental rule?

3- Or, to adhere to the last paragraph of the same article, must we accept init method as a special case and use alloc/init pattern as an exception to the fundamental rule?

Memory Management Fundamental Rule:

  • You only release or autorelease objects you own.
    • You take ownership of an object if you create it using a method whose name begins with “alloc” or “new” or contains “copy” (for example, alloc, newObject, or mutableCopy), or if you send it a retain message.
    • You use release or autorelease to relinquish ownership of an object. autorelease just means “send a release message in the future” (to understand when this will be, see “Autorelease Pools”). http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmRules.html

Allocating and Initializing Objects / The Returned Object:

However, in some cases, this responsibility can mean returning a different object than the receiver. For example, if a class keeps a list of named objects, it might provide an initWithName: method to initialize new instances. If there can be no more than one object per name, initWithName: might refuse to assign the same name to two objects. When asked to assign a new instance a name that’s already being used by another object, it might free the newly allocated instance and return the other object—thus ensuring the uniqueness of the name while at the same time providing what was asked for, an instance with the requested name.

In a few cases, it might be impossible for an init... method to do what it’s asked to do. For example, an initFromFile: method might get the data it needs from a file passed as an argument. If the file name it’s passed doesn’t correspond to an actual file, it won’t be able to complete the initialization. In such a case, the init... method could free the receiver and return nil, indicating that the requested object can’t be created.

Because an init... method might return an object other than the newly allocated receiver, or even return nil, it’s important that programs use the value returned by the initialization method, not just that returned by alloc or allocWithZone:. The following code is very dangerous, since it ignores the return of init.

id anObject = [SomeClass alloc];
[anObject init];
[anObject someOtherMessage];

Instead, to safely initialize an object, you should combine allocation and initialization messages in one line of code.

id anObject = [[SomeClass alloc] init];
[anObject someOtherMessage];

http://developer.apple.com/library/ios/#documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocAllocInit.html

like image 709
lockedscope Avatar asked Nov 25 '10 16:11

lockedscope


3 Answers

The init method doesn't receive the object; the object receives the init message. The object does not possess itself; rather, it always knows about itself (through the implicit self argument in every message).

You're right that the object does not own itself, though. If alloc and init were fused in a single new method, that method would be its own (super) caller, so it would own the object (until it returns) and so be unquestionably right in releasing it. Since they are separate, and init is not the caller of alloc, you're right that it does not own the object, so you are right to question this practice.

This is one of the few cases where it's OK for one object to release an object (in this case, itself) on behalf of another. The alternative is not to release it, which, if you're going to either return nil or throw an exception, will be a leak.

In general, anytime you have an object retain or release itself, you should feel dirty. In this specific case, it's OK, because you are preventing a bug (a leak) rather than probably creating one.

2- Also, from the same article, init could return another object or nil. So, in this case, when we use the complete pattern of alloc/init, we could not release the object returned by alloc but we could only release the object returned from init and, init releases the object it received from alloc instead of us.

But init is not a alloc,new,copy or alike method so we must not release the object returned from it as it does not give us the ownership of object.

As init releases the old object on behalf of its caller, if it creates a new object, it does that on behalf of its caller. The caller does own the substitute object that init creates or retrieves for it.

As a corollary to this, if init retrieves a previously existing object, it must retain that, so that the caller will own it.

Again examining the hypothetical* new method, it would also need to both release the old object and create (owningly) or retain the substitute.

In all of these cases, it's init acting on behalf of its caller. It's normally dodgy for one method to do another's memory management, but for these cases, init doing it on behalf of its caller is necessary.

*The new method does exist, but simply sends alloc and init, so there's no need to implement it separately.

like image 122
Peter Hosey Avatar answered Oct 31 '22 15:10

Peter Hosey


If initialization fails for some reason and must return null, then you must release the object in order to avoid leaking memory.

Similarly, init may decide to swap in a different object and return it - in that case you must also release the object in order to avoid leaking memory.

In both cases it's necessary because the original object isn't being returned by init, and will be orphaned after the method returns. Alloc has automatically retained the object, so if you don't release it its retain count will be stuck at 1 forever.

like image 3
Sean U Avatar answered Oct 31 '22 15:10

Sean U


[Would another perspective help?] The init method (and its siblings initWith... and similar) is a bit of an odd case but is not a special case of memory allocation rules. Init is odd because it has a name that sounds like it is going to change the internals of the instance but in fact it may do more than that (it may substitute some other object and initialize that object, for example). The tip-off is in the declaration of init:

- (id)  init  // the way it is

vs

- (void) init  // in some other universe

The init method returns an object, so it might have been better named something like 'return an object that is (class-wise) an equivalent object and that has been initialized'. Most methods do not perform this kind of switcheroo, which makes init a bit different/odd.

There is nothing 'magic' about the alloc / init nesting -- it's just the simplest way to handle the fact that the object that you get back from alloc may not be the same object you get back from init. This works perfectly fine:

NSSomeClass* s = [NSSomeClass alloc];
s = [s init];  // that 's =' part is really important ;-)

and is exactly equivalent to the 'standard' idiom:

NSSomeClass* s = [[NSSomeClass alloc] init];

This is potentially problematic:

NSSomeClass* s = [NSSomeClass alloc]
[s init];   // bad! you ignored the result of init

The implementation of an init method must be done particularly carefully when the implementation returns a different object than the one it receives as the incoming 'self'. In such a case the init method takes on the responsibility of memory management of the 'self' object (because it's not going to return that object - so who else could be expected to do the management?)

It's possible to do some pretty ugly trickery, BTW. Don't try this at home!

// don't do this!
S* s = [S alloc] 
[s retain]; // needs to survive 2 init's
S* ss = [s init......];  // s.retain goes 2-->1
S* sss = [s init.....];  //  ... goes 1-->0;

Doing this is extremely poor practice because it depends on the init..... method always returning a new object (instead of the one it receives). That's an obviously bad assumption!

NOTE that by "a method receiving an object in 'self'" I mean that the method was invoked upon/by an object, and that object is made available by convention through the 'self' pointer.

like image 1
Art Swri Avatar answered Oct 31 '22 14:10

Art Swri