Reunification

2010-02-02 20:15:00 UTC

A common question from people new to Objective-C is “why do I have to separately alloc and init? Why can’t I just call one thing and get a fully initialized object?”.

It’s certainly not how other environments do it. Usually, you call one function or class method, and you get an object. Cocoa makes you send two messages: one to the class, then another to the fresh, uninitialized instance you got back. What does Cocoa know that those other environments don’t?

There are two reasons why alloc and init are separate:

  • “NSResponder”* says that sometimes, NeXT wanted to initialize the same object multiple times.

    This is interesting, but horrifying to modern readers. Practically all init methods I’ve seen do not handle the case of initializing the already-initialized; they blindly assign newly created/retained objects to the ivars without checking for existing objects there.

    A bigger problem is when the init method releases the receiver and returns some other object; in the second message, the “other” object may in fact be the receiver (returned from the first message), so [self release] this time would be killing off an object that should stay alive. On the other hand, retaining the object before re-initializing it will create a leak if the object doesn’t release itself. Unpredictable behavior is bad.

  • On the same Stack Overflow question, Darren points out that alloc is shorthand for allocWithZone:; if allocation and initialization were merged in Cocoa, then, to maintain the same functionality, every initializer would need to take a zone parameter, and optionally come in a flavor without one. You can imagine how a proliferation of initializers would ensue.

    This is essentially what the documentation says: “Separating allocation from initialization gives you individual control over each step so that each can be modified independently of the other.” I.e., both steps can be customized, so they must remain separate so that you can customize either or both.

So, there are two practical reasons why Cocoa’s designers separated allocation from initialization.

But let’s revisit this with modern eyes:

  • As I mentioned, in modern Cocoa, initializing an instance again is risky at best. In practice, I don’t think anybody does it, and if anybody does, they probably feel guilty about it and mean to refactor that code “someday”.
  • Nobody uses zones anymore.

That blows those two reasons away.

So, let’s imagine what life would be like if they had never existed in the first place, and allocation and initialization had remained one task:

+ (id) newWithFramistan:(Framistan *)framistan {
    id obj = [super new];
    if (obj) {
        obj->framistan = framistan;
    }
    return obj;
}

Right away, you can see several advantages:

  • We can name our local variable “framistan”, not “newFramistan” or anything like that.
  • No more assignment in condition (if ((self = …))).
  • No more assignment to self (which should make Wil Shipley happy).
  • Eliminates the problem of [super init] returning a different object, whereupon you would have to release the receiver. Since the receiver is the class, there is nothing for the upstream-initialized object to be different from. If the superclass wants to return an existing object, it can just do that.
  • Related to the first point, we no longer have a name conflict between instance variables and local (especially argument) variables. In init methods, we resolve this conflict with style rules, such as the aforementioned newFramistan for locals or m_framistan for instance variables; in this newWithFramistan: method, the conflict doesn’t exist in the first place.
  • There is never an uninitialized object that the caller could leave lying around. We’ve all seen (or written) code like Foo *foo = [Foo alloc]; [foo init];; in this alternate universe, such faux pas are impossible.

There are trade-offs:

  • It’s one line longer. (Depending on your employers’ level of cluefulness, this may be a small advantage.)
  • Since you’re not in an instance, you have to prefix every instance-variable access with obj->. I can see how this could get tedious; on the other hand, this is what prevents ivar-vs.-local name conflicts.
  • If you use object controllers that automatically prepare their content, they’ll use alloc and init, bypassing this initializer. I’ve never used the automatic-content-preparation feature, so this doesn’t affect me.
  • It’ll be less than familiar to anybody who hasn’t read this post, and it’s certainly a change from currently-typical Cocoa code.
  • Doing this in a subclass of a class that has an unusual designated initializer, such as NSView with its initWithFrame:, could get tricky.

My opinion is mixed. On the one hand, real problems with the init way of doing things are rare for anyone who knows the ropes. On the other hand, it sure is more straightforward, isn’t it?

What do you think?

* Presumably the same one most of us recognize as John C. Randolph, although there’s no direct evidence of this on his SO profile.

17 Responses to “Reunification”

  1. HS Says:

    One more downside: Batch allocation is not an option.

    With the alloc/init pattern it’s a possible performance win if you are sufficiently clever.

  2. Carl Says:

    Is this similar to how Python has __init__ (which everyone uses) and __new__ (which only deep magic wizards use)? The __new__ method carves out space for an object of type myclass, and then __init__ initializes the properties of the new myclass instance. If your class is immutable, then the initializing step is going to have to be folded back into the __new__ phase.

  3. Peter Hosey Says:

    Carl: I don’t think immutability has anything to do with it; you could initialize an immutable instance (not to be changed from its initialization values) in __init__ just fine.

    And yes, from what you’re saying, that __new__/__init__ distinction sounds about the same. There are different sets of advantages and disadvantages to merging them, though, and I don’t know enough about __new__ to be able to say what they are for Python.

  4. Eimantas Says:

    “Cocoa Design Patterns” book covers this question very broadly and explains all the magic and motivation behind this approach. I recommend it to all the Objective-C noobs .)

  5. Peter Hosey Says:

    I have a low opinion of talk of “design patterns”, and especially of whole books of design patterns.

    A “design pattern” is only useful as a recipe to consider following when constructing your own classes. Thus, the two design patterns that matter in Cocoa are MVC and delegation. Both of these are described very well for free in Apple’s docs.

    So any design patterns book is going to waste time and toner on a bunch of stuff that, at least as a Cocoa programmer, you’ll never use. Design patterns people will probably count notifications as pattern #3, but you’ll never write a notification center in Cocoa, because you already have one. I see no point to writing up “design patterns” to describe classes that you already have.

    My experience is that the two patterns I’ve named above are enough. Anything else that I’d use either follows from MVC, is probably too complex for my own good, or is probably more low-level than I care about.

    I’ve skimmed the book you refer to, Eimantas, and I didn’t see anything in it that looked useful that I didn’t already know. For new Cocoa programmers, there’s a chapter in the Cocoa Fundamentals Guide that covers the same topic (design patterns in Cocoa) for free.

    I’d love to read a description of exactly what useful information is in this book that I missed and that isn’t covered in the Cocoa Fundamentals Guide. Perhaps its explanation of why alloc and init should remain separate, but I’m not going to buy a whole book just for that.

  6. Andy Lee Says:

    Peter, to an extent I share your skepticism about the design patterns meme. I could never get through more than a few pages of the Gamma book without feeling I was wasting my time. I’ve never been quite sure why, because I *like* organizing principles.

    On the other hand, even if you know all the stuff in the Buck and Yacktman book, and even if the same material is available in the free docs, that doesn’t mean there’s no merit in presenting or organizing that material in a different way. (Many Cocoa beginners swear by the Hillegass book, for example.) If readers can see more of the design behind the frameworks, they may be less likely to fight them. To me this would be a good thing, because I sometimes see newcomers on cocoa-dev who seem to think Apple threw stuff together just to be different from what they’re used to.

    I have the Buck and Yacktman book and frankly I’ve only picked at it here and there. I tend to not be crazy about books that thick and verbose. Sell me three thinner, breezier books and I’ll be happier and you’ll make more money besides! :) The book did help me discover the “template method” pattern, which, yes, is described in the Apple docs, but I hadn’t noticed it.

  7. Andy Lee Says:

    Getting back on topic — your pros and cons make sense to me. I’d only add that going to a +newXXX approach would feel strange, for a while at least, because alloc/init is so ingrained in my muscle memory. Also, I feel like instance initialization is conceptually a behavior of instances, not classes, but again maybe that’s just from years of conditioning.

  8. Peter Hosey Says:

    Andy Lee:

    … even if you know all the stuff in the Buck and Yacktman book, and even if the same material is available in the free docs, that doesn’t mean there’s no merit in presenting or organizing that material in a different way.

    Good point.

    I’d only add that going to a +newXXX approach would feel strange, for a while at least, because alloc/init is so ingrained in my muscle memory.

    Yup, same here.

    Also, I feel like instance initialization is conceptually a behavior of instances, not classes…

    Also a good point.

  9. Karl Adam Says:

    This example overlooks class clusters, which leverage the separate allocation and initialization to provide a stand-in class until the usage is determined and a proper subclass is returned. With separate allocation you can return a generic subclass that will instantiate the correct subclass for you upon init. While you could bake all that logic into your +new function, I feel that would defeat some of the fun of the runtime lookup you could leverage in returning the appropriate subclass. In the class cluster case, I can only imagine this +newIsm growing larger than the same in the old system, especially when you take into account the ability to add your own classes to a class cluster.

    As to the Cocoa Patterns book, I’m also not a fan, but it’s purpose isn’t in telling you what the docs already told you, but rather in explaining why the patterns used in Cocoa are there, and that may be valuable for those that want to know more about the programming environment in which they work.

  10. Colin Barrett Says:

    Great post, Peter.

    Just weighing in on the Buck & Yacktman book, the only experience I had was flipping through it once at NSCoder Night SF. I noticed they had whole chapter on NSZone and quickly shut the book and handed it back to its owner. Not a fan.

  11. Johan Kool Says:

    The book “Cocoa Design Patterns” by Buck & Yacktman is actually my favorite Cocoa book so far. I think it offers more insights than many other books, even though I feel quite familiar with most of what is discussed. It did feel more geared towards the more advanced Cocoa developer. At least it doesn’t spent half its chapters explaining me how to hook up a button in Interface Builder.

  12. Charles Parnot Says:

    I agree it’s good to revisit some of the established designs, as one can often find the times have changed and the initial rationale is not there anymore.

    In the end, and paraphrasing some of the excellent comments in this thread, I really like the simple justification that allocation and initialization are 2 separate concepts that deserve separate handling and happen at the class and instance level. Class cluster show how this separation allow for elegant implementations, and excellent encapsulation, for some of the more complex Cocoa classes.

    I think the slight increase in the learning curve and the potential for some initial confusion at this pattern, is worth the extra flexibility.

  13. Darren Says:

    Here’s a modern example of why it’s still important to separate allocation and initialization in Objective-C:

    http://stackoverflow.com/questions/2199106/thread-safe-instantiation-of-a-singleton/2202304#2202304

  14. Sachin Says:

    What happens if I don’t alloc, init and assign value straightway. I experimented with NSString and it seems to be working fine. I did this – NSString *urlString =@”http://wikipedia.org/”;

    Why compiler does not give error? What can go wrong if use without alloc, init?

  15. Peter Hosey Says:

    Sachin: A literal like that is a special case: The object already exists at compile time, so there is no need to create it at run time. Same way you don’t need malloc to create a C string if you can just put it in as a literal.

    Literal strings are also special in that they exist despite nothing owning them. You can own a literal string by retaining it, and you can release this, and you should try to keep these balanced just because your app is poorly designed if you don’t, but no consequences will ensue if you over-retain or over-release a literal string. Only strings created at run time can be deallocated, leaked, or prematurely deallocated.

    There are no other classes you can do this with, since NSString is the only one that gets a literal syntax in Objective-C.

  16. Sachin Palewar Says:

    Hey Peter,

    Thanks a lot for your quick response. So are you saying its perfectly ok to use such string literals without alloc, init? And app won’t be poorly designed if I don’t do alloc, init in case of string literals?

    I have one more question:

    I also follow my NSString declaration with following 2 lines:

    NSURL *url= [NSURL URLWithString:urlString];

    NSURLRequest *urlRequest=[NSURLRequest requestWithURL:url];

    [self.webBrowser loadRequest:urlRequest];

    Again I haven’t used alloc, init but webview seems to load URL fine. How does that work? Is it a poor design?

  17. Peter Hosey Says:

    It isn’t possible to create a string literal using alloc and init. And yes, it’s fine to simply use the literal string and not alloc and init a second string from it.

    I also follow my NSString declaration with following 2 lines:

    NSURL *url= [NSURL URLWithString:urlString];
    NSURLRequest *urlRequest=[NSURLRequest requestWithURL:url];

    [self.webBrowser loadRequest:urlRequest];

    Again I haven’t used alloc, init but webview seems to load URL fine. How does that work? Is it a poor design?

    No, there’s nothing wrong with this. These are convenience constructors. The documentation describes them in detail.

Leave a Reply

Do not delete the second sentence.