Finally Waving The White Flag to Objective-C 2.0
For a long time (including the entire development of touchDefense), I was against the concept of Objective-C 2.0. Not that I hate changes, but it seemed a bit arbitrary for Apple to take it on itself to modify Objective-C. Certainly no other company has the same level of investment in ObjC, but Apple doesn't have the same legal stranglehold on ObjC that Microsoft has on .NET languages. It just felt a bit wrong to me.
But when I began working on my newest iPhone application, I decided to go ahead and make the transition to writing my code using the syntactic confections sold at the Objective C 2.0 bakery.
Ideologies be damned, I'd rather fill up on gas in West Virginia again than write another unnecessary mutator or accessor methods. From a long-time anti-ObjC 2.0er, these are the reasons I decided to gorge upon the sweet syntatic sugar.
@property
. @property
. @synthesize
.
I don't like the idea of properties, which strikes me as creating unnecessary ambiguity about what you're actually doing1. But, I do love not having to type as much boilerplate code.
Using properties is pretty easy. Take this ObjC 1.0 interface:
@interface Person : NSObject {
NSString * name;
int age;
NSMutableArray * friends;
}
-(void)setName: (NSString *)aName;
-(NSString *)name;
-(void)setAge: (int)anAge;
-(int)age;
-(void)setFriends: (NSMutableArray *)someFriends;
-(NSMutableArray)friends;
@end
which has this implementation:
@implementation Person
-(void)dealloc {
[self setName:nil];
[self setFriends:nil];
[super dealloc];
}
-(void)setName: (NSString *)aName {
if (name != nil) [name release];
name = [aName copy];
}
-(NSString *)name {
return name;
}
-(void)setAge: (int)anAge {
age = anAge;
}
-(int)age {
return age;
}
-(void)setFriends: (NSMutableArray *)someFriends {
if (friends != nil) [friends release];
friends = [someFriends retain];
}
-(NSMutableArray)friends {
return friends;
}
@end
Now that isn't really that hard to write, but after you've written it a hundred times you're probably had enough. Using ObjC 2.0 and properties you can rewrite the interface to look like this:
@interface Person : NSObject {
NSString * name;
int * age;
NSMutableArray * friends;
}
@property(readwrite,copy) NSString * name;
@property(readwrite,assign) int age;
@property(readwrite,retain) NSMutableArray * friends;
@end
But the real win is for the implementation:
@implementation Person
@synthesize name, age, friends;
-(void)dealloc {
[self setName:nil];
[self setFriends:nil];
[super dealloc];
}
@end
Ahhh. That's so much shorter. We do still have to write a dealloc method2, but we've compressed a dozen lines of tedious code into one, and that's a real win for me.
Beyond magically creating mutators and accessors, you can also use attributes as an abbreviation for a field's mutators and accessors. Some people may find this to be useful, but to me it seems at best neutral, and didn't weight strongly for my switching to 2.0.
// is this
Person * p = [[Person alloc] init];
p.name = @"Will";
p.age = 23;
p.friends = [[[NSMutableArray alloc] init] autorelease];
NSLog(@"Person(%@, %d, %@)",p.name,p.age,p.friends);
// really better than this?
Person * p = [[Person alloc] init];
[p setName:@"Will"];
[p setAge:23];
[p setFriends:[[[NSMutableArray alloc] init] autorelease]];
NSLog(@"Person(%@, %d, %@)", [p name],[p age],[p friends]);
I don't buy it. Not saying it's worse, but I think that one Swiss Army chainsaw is enough, thank you.
I like Iterators, too.
After properties and synthesize, my next favorite addition has to be iterators. I'm glad the genius of Perl, Python, Ruby, Java 1.5--pretty much every language in heavy usage--has come to Objective-C.
In lines of code, the difference between
int i, int l = [people count];
for (i=0; i<l; i++) {
Person * p = [people objectAtIndex:i];
NSLog(@"age: %d", [p age]);
}
and
for (Person *p in people) {
NSLog(@"age: %d", [p age]);
}
is pretty minimal. But I think it's a huge improvement. First, manually iterating over lists is a great place for catastrophic typos to occur. But more importantly, when I'm in the zone cranking on some code, writing an old-style loop breaks my mental stride much more than the new-style loop. Even if I happen to throw in a typo it'll be something the compiler will catch, whereas with the old-style I have to be more attentive to the syntax itself (instead of maintaining my focus on what the loop is accomplishing).
Everybody's Doin' It.
The last reason I moved to Objective C 2.0 is momentum. Most new projects and new developers (of which the iPhone has brought many) are using ObjC 2.0, are writing example snippets with ObjC 2.0, and are happy with ObjC 2.0. There are some ideological wars I think are worth fighting, but fighting to keep a programming language more open on a completely closed platform just doesn't feel like one of them.
So in the end, I suppose it really didn't take much at all to get me to jump off my high horse. It was probably a bit silly of me to climb on in the first place. Is anyone else still fighting the good 1.0 fight, or did you have different reasons for deciding to switch to 2.0?
I have the same complaint against the Python attributes. They are a neat way of simplifying syntax, but I think they violate the principle of least surprise in a major way. I want to--and do--use them, but I feel dirty afterwards. Like I had just killed a turkey with my bare hands only to be reminded that we actually needed a chicken instead. Damn it.
Ahem. While we're sidetracked, I've never been quite sure how the language famous for monkey-patching also championed the principle of least surprise. It'd be like Java heralding concise syntactic elegance. But ad hominem attacks aside, it's a damn fine principle, even if it often contradicts the principle of doing cool shit to make friends and influence Redditors.↩
"Wait you ignorant fool," says Angry Commenter, "You can use garbage collection. How do you even turn on your computer if you're so dense?" Well, I do mostly iPhone development, which doesn't support GC, so the garbage collection stuff doesn't do much for me in general, and had nothing to do with why I belatedly switched from ObjC 1.0 to 2.0.↩