Apple announced Objective-C 2.0, the first major change to the language ever. The features should all be great—support for properties, inbuilt iterator syntax, much higher abstraction in the runtime, and more—but ultimately, though many of these modifications attempt to solve real problems in Objective-C, they nearly universally solve them inelegantly and inconsistently.

One of my biggest complaints comes with the iterator extension. In Objective-C 1.0, to iterate over a collection, you had to use the Java 1.4 method: you got an NSEnumerator object, then repeatedly called -[NSEnumerator nextObject] until there were no elements left in the collection. Objective-C 2.0 introduces a new iterator object, NSFastIterator, and modifies the for iterator in C so that you can iterate over Cocoa collections using NSFastIterator, basically using the same general concept as Java 5 for its implementation.

I find this solution fundamentally flawed. Objective-C, until now, has always been extremely good about not modifying the C core at all. Objective-C syntax extensions were always deliberately alien to C so that there could be no confusion. Keywords were prefixed with @ (e.g., @selector(objectEnumerator), @interface, @implementation), method calls [lookLike this], and so on, so the logical way to add iteration to the language, if you’re going to do it that way, would be to add an @foreach keyword. Instead, Objective-C 2.0 overrides the for operator, so that you can now write

for (NSString *string in anArray) {}

This needlessly confuses ObjC and C features, and sets a very bad precedent: Objective-C features can be now intermingled with classical C.

This complaint, though, sidesteps an even bigger complaint: iterators are, at their core, trying to be a special, niche version of generic lambda blocks—in this case, a block that takes one argument and does something with it. The “proper” solution then, rather than adding a @foreach keyword, would be to add blocks/lambda methods to Objective-C.

Currently, Objective-C and .NET basically take the same tack for callbacks: you can pass around a method, but that method must exist and be named. So, for example, in ObjC we have

[[NSNotificationCenter defaultCenter] addObserver: self
                                         selector: @selector(fooDidHappen:)
                                             name: NSSomeNotification
                                           object: nil]

which, if .NET had NotificationCenter, would be equivalent to

NotificationCenter.defaultCenter.addObserver(this,
                                             delegate(fooDidHappen),
                                             SomeNotification,
                                             null)

We see the same idiom anywhere that we require a callback. What I want, instead of passing an object and a method, is to be able to pass an inline function. If I had that, then the for modification would be unnecessary; I could simply add a method -[NSCollection eachObjectDo:] that took a block and passed it each element of the collection in sequence. Sadly, not only is this not the path Apple took, but there’s no indication that it even almost made the cut. Surprisingly, Microsoft will be adding this feature to .NET 3.0, and so .NET 3.0 will be superior to Objective-C 2 in this regard.

Objective-C 2.0 also adds properties. This has me fundamentally irked. Joel has complained of the problems with leaky abstractions. To be honest, though I agree with most of his individual points, I disagree that abstractions are dragging us down; every single abstraction in the computer except raw wires are abstractions, all are at least somewhat leaky, and all that are properly designed, in my opinion, help much more than they hurt.

Properties at first seem like a good abstraction, one that helps more than hurts. Objective-C rigidly adheres to Smalltalk’s concept of how visibility should work: using Java terminology, all instance variables are protected, all methods are public. This has clear implications in how you think about your program. Historically, the inconvenience of getting instance variables has strongly encouraged a tell-don’t-ask design philosophy. When you do ask or assign, it’s clear that what you’re doing may have side-effects: [foo setBar: baz] is clearly a method invocation, and I should expect that side-effects may result.

With Objective-C 2.0, we gain .NET-style properties, and have assigned foo.bar = baz as the syntax. This is another example of Objective-C now modifying C syntax, only this modification is not simply inconsistent; it’s deadly. Firstly, the above code implies to any decent C programmer that foo is a struct. Just from the above code snippet, though, we actually have no idea. It could either be a struct or a class. Or, hell, it could be a union. Now we have three radically different concepts sharing exactly the same syntax. Worse, though, to most programmers, the above syntax implies a lack of side-effects beyond the value of foo.bar changing. After all, that’s the behavior of both struct and union (and classes in C++, for that matter). But since, as in .NET, Objective-C properties are actually full-blown methods, we have no guarantee that further side-effects won’t occur, and in fact we see this happen in Apple code. One of the demos they have for Core Animation shows how easy it is to make an object fade out. All you do is

someObject.opacity = 1;

This doesn’t simply set the value of some opacity variable, but actually begins an animation of that object’s fade-out in a new thread. Not only is this not what I would consider the expected behavior, but it also means that, for example, you can throw one of NSThread’s exceptions within a variable assignment. This is a leaky abstraction that, in my opinion, does far more harm than good.

These complaints aren’t specific to Objective-C 2.0; they apply equally well to Java 5, .NET, Delphi, and other similar systems. So these mistakes aren’t new, and I’m actually somewhat in the minority for considering them mistakes to begin with, but I’m sticking to my guns: these are horribly leaky abstractions solving a non-problem. I would rather have seen any of three alternatives:

  • Emphasize that you can access ivars directly by allowing foo.bar in addition to foo->bar. I don’t like this solution, but at least it makes foo.bar = baz do what I’d expect it to.
  • Assign a different syntax to properties, perhaps someObject!xCoordinate = foo, [someObject xCoordinate = foo], or any syntax other than dot notation so that programmers have at least an inkling that this is not their daddy’s dot operator.
  • Implement @property so that it only generates -[MyObject setFoo:] and -[MyObject foo] methods and nothing else. Shelve the whole dot syntax. This cuts down how much code we have to write, but doesn’t provide us with a false view of how things actually work. In my opinion, this is the best of both worlds.

Sadly, Objective-C seems content to repeat the mistakes of its predecessors. This does not herald the wonderful new direction for Objective-C that Apple would like to have you believe; it heralds the beginning of the end of Objective-C’s uniqueness.