Why are alloc and init called separately in Objective-C?
Asked Answered
D

7

15

Note: I'm relatively new to Objective-C and am coming from Java and PHP.

Could someone explain to me why I always have to first allocate and then initialize an instance?

Couldn't this be done in the init methods like this:

+ (MyClass*)init {
    MyClass *instance = [MyClass alloc];
    [instance setFoo:@"bla"];

    return instance;
}

+ (MyClass*)initWithString:(NSString*)text {
    MyClass *instance = [MyClass init];
    [instance setFoo:text];

    return instance;
}
...

Is this just a relict from the old C days or is there something that I'm not seeing?

I know this isn't a problem as I could as well always call alloc and init, but since it's a bit tedious I'd like to at least know why I'm doing it.

I'm liking the expressiveness of the language so far, but this is something that I want to fully understand in order to think the Objective-C way.

Thank you!

Discobolus answered 6/9, 2009 at 11:11 Comment(0)
T
21

+new ends up sending an +alloc message to the class and an -init message to whatever comes back from +alloc.

The reason that NeXT departed from Stepstone's convention of using the +new message (which was a Smalltalk idea) is that early on, they encountered situations where they wanted to be able to initialize the same object more than once.

Toadeater answered 6/9, 2009 at 11:23 Comment(12)
That's pretty much the answer I suspected. Is it still possible to initalize objects multiple times or was that just the idea they had in mind when creating it? Doesn't sound like such a good idea to me.Division
There's a radical idea the iPhone community could bring back. ☺Phrenic
André Hoffmann: No, it's not a good idea to send an object multiple init messages unless that class's documention explicitly says it's safe. (Definitely do not try this with class clusters.)Phrenic
You are not promised that any instance you create will get only one -init message. Code defensively. This applies to +initialize, too.Toadeater
No, it doesn't. The runtime explicitly guarantees that it will send you exactly one +initialize message, even across threads. And I cannot think of a good reason to do [super initialize]—you're not making sure it won't return nil, since it returns void, and you are guaranteed that your superclass will receive initialize before you do.Phrenic
References: developer.apple.com/mac/library/documentation/Cocoa/Reference/… and developer.apple.com/mac/library/documentation/Cocoa/Conceptual/…Phrenic
Peter, that document says that the Obj-C runtime will only send it once. That does not rule out any other code doing so, and in fact there's no way to prevent it. -init and +initialize should both be written to tolerate multiple invocations.Toadeater
I still cannot think of a good reason to explicitly send a class an +initialize message. If you do, then you have written a bug. And the documentation under NSObject explicitly says “it is invoked only once per class”. Therefore, if you're going to account for the possibility at all, you should do so by keeping track of how many times you've been called and asserting that it's 1.Phrenic
The doc says it's invoked only once per class by the runtime. I will tell you as a former Apple DTS engineer, that you should not rely on it only being invoked once.Toadeater
The NSObject doc says, under Special Considerations, “initialize it [sic] is invoked only once per class.” If anything else calls the same class's initialize more than once, that is a bug, going by everything the documentation both says and implies. If it is not a bug, then either the documentation is omitting something, or it contains an explicit warning about multiple initialize messages that I'd like to see.Phrenic
+initialize very much can be executed more than once per class. 500+ characters isn't enough to demonstrate, so here you go: friday.com/bbum/2009/09/06/…Lachesis
This fact should be documented specially because the documentation is making people confusing. Doc is complete in logic, but it's not enough for general developers who didn't experienced runtime design consideration and the real situations like this...Lone
P
16

Because creating an instance and initializing an instance are two separate jobs.

You send an alloc message to the class to get an uninitialized instance. You must then initialize the instance, and you often have several ways to do that. For example:

myStr = [[NSString alloc] init]; //Empty string
myStr = [[NSString alloc] initWithFormat:@"%@.%@", parentKeyPath, key];
myStr = [[NSString alloc] initWithData:utf16data encoding:NSUnicodeStringEncoding error:&error];
myStr = [[NSString alloc] initWithContentsOfURL:URL encoding:NSUTF8StringEncoding error:&error];

Each of these initializes the string in a completely different way. How you initialize the string depends on what you want to initialize it from.

Of course, nobody likes writing alloc and then init and then autorelease every time, so you usually have convenience methods (e.g., stringWithFormat:) that do all three steps for you.

Edit: For more on this topic, including essential insights from commenters, see my blog post “Reunification”.

Phrenic answered 6/9, 2009 at 11:18 Comment(7)
I see that, but if the initX methods would call alloc for you, every call to alloc could be removed from the example you provided.Division
As Mehrdad points out in his comment on his answer, hiding the allocation of an object is not a good practice. If you alloc it, you must release it. This isn't so intuitively-obvious if you replace alloc with init in the rule: “If you initialize it, you must release it”? Why?Phrenic
Well that sound to me just like a way of making the developers aware of deallocation. But there should be other ways to do that(like a documentation) than writing a lot of code.Division
Clear method names are always better than documented unclear method names, which is what an initWithX: that doesn't just initialize is. You may very well not read the documentation (didn't know it exists, forgot to read it, promised to read it later, tried to read it but were undercaffeinated), but you cannot get away without reading the code. Documenting methods' behaviors is nice, but clear method names are essential.Phrenic
The best examples among Apple's APIs have such clear class and method names that you can skip all the tutorials and go straight to the class reference, and be able to figure out pretty quickly how to do whatever it is you need to do.Phrenic
Well how about [class allocAndInitWithX:...]?Division
What's the advantage of that over [[class alloc] initWithX:]? If you're worried about the cost of a message, stop; you are prematurely optimizing. Messages are not that expensive anymore, and haven't been for years.Phrenic
K
12

See NSZone.

+alloc is a shortcut cut for +allocWithZone:, which is a mechanism Cocoa provides for optimizing memory allocation.

So you have the option to do something like this:

foo = [[NSString allocWithZone:MyZone] initWithString:@"Foo"];
foo2 = [foo copyWithZone:MyZone];

The idea behind memory zones is that if you have a large number of similar objects that are frequently allocated and deallocated it may more efficient to use a separate memory zone for those objects.

In order for zoning to be effective you'd want to have +allocWithZone: available to every NSObject subclass, hence you need to separate allocation and initialization. You can create and use all the shortcuts you want, like +new, but underneath it all you need an -init method that initializes an object that has already been allocated.

Kristinkristina answered 6/9, 2009 at 16:17 Comment(2)
You could, but you wouldn't. You wouldn't have initWithZone: precisely because there are zones. Otherwise, if alloc and init were always combined into a single step then every single initializer you write and use would have to include a Zone parameter. In other words, allocation and initialization are separated because allocation can be customized.Kristinkristina
The idea behind zones was that you could speed up deletion of memory by blowing away an entire zone instead of having to free each object in it individually. Very few people ever used them, though.Toadeater
A
7

"Separating the allocation and initialization stages of instance creation provides many benefits. It’s possible to use any variation of the +alloc class method to allocate an instance and then use any available initializer with the new instance.This makes it possible to create your own initialization methods without needing to provide alternate implementations of all allocation methods. New allocation methods are seldom created because the existing methods meet almost every need. However, one or more new initializers are created for almost every class. Due to the separation of allocation and initialization stages, initializer implementations only have to deal with the variables of new instances and can completely ignore the issues sur- rounding allocation.The separation simplifies the process of writing initializers. Furthermore, Cocoa standard initializers like -initWithCoder: work with instances regardless of the way memory for the instance was allocated. One negative consequence of the separation of allocation and initialization is the need to be aware of conventions such as the designated initializer.You must know which methods are designated initializers and how to create and document new initializers in sub- classes. In the long run, using designated initializers simplifies software development, but there is an argument to be made that theTwo-Stage Creation pattern adds to the early learning curve for Cocoa developers."


(c) Cocoa Design Patterns by Erik M. Buck and Donald A. Yacktman

Amoeboid answered 16/10, 2010 at 14:39 Comment(1)
As a 20+ year C++ programmer and a new Objective-C programmer I too question the reason for separation of allocation and initialization. When I learned C++ one of its advantages was that it combined allocation and initialization. Conventional wisdom being that not initializing a data structure properly or at all is a common programming error. C++ allowed me to create classes that enforced calling an initializer(constructor) defined within the class, anytime an object is allocated. And it also allowed me to overload "new" if I wanted custom allocation. Most modern languages agree.Martlet
T
6

You don't have to. You can use [MyClass new]. This is similar to your hypothetical init method.

Basically, Objective-C, which didn't have garbage collection initially, separates the concept of memory allocation and class initialization. That's why there are two distinct methods. When you call alloc, you are explicitly allocating memory.

Tendinous answered 6/9, 2009 at 11:13 Comment(2)
I haven't seen that yet. I suppose it's not very common? Also there are no methods like newWithString: or similar so most of the time I can't really use that. My question is more like: Why is no one doing it the way I proposed?Division
No, this works only for [[Class alloc] init] pattern. As I mentioned in my answer, it's considered a good thing to make memory allocation explicit in a manual memory management environment. By separating the concept of memory allocation to alloc method, you're always conscious that you should release it.Tendinous
E
1

Most classes have what you are asking for. You have gotten answers before on why this is like it is and why you wouldn't always want to use this all the time but if you read the documentation to classes you will see many class methods that act this way and they are often used.

For NSString you have, for example:

+ (id)string  // (Empty string)
+ (id)stringWithFormat:...  // Formatted string (like you use)
+ (id)stringWithContentsOfURL:... // String populated with contents of URL

And so on. And you would then use this like: NSString *myString = [NSString stringWithFormat:@"Hello %@\n", userName];

Most other classes have this, like NSArray:

+ (id)array
+ (id)arrayWithContentsOfFile:...
+ (id)arrayWithContentsOfURL:...
+ (id)arrayWithObjects:...

You just need to read the documentation. :) And read the other replies on why you don't want to use this too much.

Efflorescence answered 6/9, 2009 at 12:9 Comment(2)
These convenience methods are different from the alloc/initWithX combo in that they autorelease the returned objects, which is not an insignificant difference.Workman
I totally forgot about that. :)Efflorescence
W
0

alloc : Memory is allocated/given to the object-reference. Now reference has the possession of the memory but has not done anything yet. This memory be empty(rarest case) or with some anonymous data.

alloc and init : Allocated memory is cleaned/emptied. Memory is initiated by zero bit.

alloc and initwithdata... : Allocated memory is initiated with desired data respected to properties of the class.

For example when you purchase a plot you get the possession. This plot is given to you as it is, ruined bricks or old house may be there. This is alloc.

When you clean your plot and remove all dirt and litter. This is alloc with init.

When you build that into some valuable house it becomes more meaningful to you. And it is alloc initwith...

Wasp answered 23/12, 2015 at 9:18 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.