views:

1160

answers:

6

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!

+2  A: 

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.

Mehrdad Afshari
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?
André Hoffmann
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.
Mehrdad Afshari
+13  A: 

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”.

Peter Hosey
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.
André Hoffmann
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?
Peter Hosey
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.
André Hoffmann
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.
Peter Hosey
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.
Peter Hosey
Well how about [class allocAndInitWithX:...]?
André Hoffmann
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.
Peter Hosey
You are right. It doesn't make a difference.
André Hoffmann
+10  A: 

+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.

NSResponder
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.
André Hoffmann
There's a radical idea the iPhone community could bring back. ☺
Peter Hosey
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.)
Peter Hosey
That's what I thought.
André Hoffmann
You are not promised that any instance you create will get only one -init message. Code defensively. This applies to +initialize, too.
NSResponder
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.
Peter Hosey
References: http://developer.apple.com/mac/library/documentation/Cocoa/Reference/Foundation/Classes/NSObject_Class/Reference/Reference.html#//apple_ref/occ/clm/NSObject/initialize and http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/Multithreading/ThreadSafetySummary/ThreadSafetySummary.html#//apple_ref/doc/uid/10000057i-CH12-123234
Peter Hosey
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.
NSResponder
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.
Peter Hosey
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.
NSResponder
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.
Peter Hosey
+initialize very much can be executed more than once per class. 500+ characters isn't enough to demonstrate, so here you go:http://www.friday.com/bbum/2009/09/06/iniailize-can-be-executed-multiple-times-load-not-so-much/
bbum
Wow. I stand corrected (with evidence). Thank you.
Peter Hosey
+1  A: 

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.

Jon Gretar
These convenience methods are different from the `alloc`/`initWithX` combo in that they autorelease the returned objects, which is not an insignificant difference.
outis
I totally forgot about that. :)
Jon Gretar
+6  A: 

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.

Darren
There's no reason that you couldn't have initWithZone: do both though...
Graham Lee
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.
Darren
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.
NSResponder
A: 

"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

Diejmon