I've been writing some iPhone apps recently and have thus been re-exposed to the horror that is Objective-C. I've written plenty of Objective-C over the years, but coming back to it from proper modern languages like Java, C# and Ruby is just painful. It's really showing it's age and I harbour a hope that the macruby work Apple's doing right now is actually going to become the official way to write Mac and even iPhone software over the coming couple of years. That would be just perfect as the underlying Objective-C stuff would still be there for compatibility's sake and performance where necessary. Go on Apple, you know you want to!
Anyway, that mini-rant was not what I came here for. I've been wrestling with the best way to name and use instance variables. Version 1 was to name them in no special way at all, but then you end up with confusion between properties and the underlying variable and also with methods that want to use a parameter with the same sensible name (as is often the case with init methods):
@interface Person : NSObject
{
NSString *name;
}
@property(nonatomic, retain) NSString *name;
@end
@implementation Person
@synthesize name;
// This gives a compiler warning because parameter name masks the instance var.
// Calling the parameter anything else is ridiculous though.
– (id)initWithName:(NSString *)name
{
…
// Need to remember to use self.name rather than name if we want to use the property.
// It's easy to accidentally just assign directly to name and get a messed up retain count.
self.name = name;
….
}
@end
So I've decided it's probably superior to use a prefix for the instance var itself, and though I'd always used m in the past, I'm going for _ here because it seems to be an Objective-C convention:
@interface Person : NSObject
{
NSString *_name;
}
@property(nonatomic, retain) NSString *name;
@end
@implementation Person
// Note that we have to use some extra syntax here to hook up the non-underscored property.
@synthesize name = _name;
// Now the param doesn't clash with anything.
– (id)initWithName:(NSString *)name
{
…
// Property works exactly as before, but direct assignment with _name = name really
// stands out so we're only likely to do it knowingly.
self.name = name;
}
@end
Finally I end up agonising over whether I should always access my instance variables via a property (self.whatever), or whether I'm OK to use them directly (_whatever). Some purists argue that it's worth encapsulating with a property and always using that property. This way you can change your underlying data types but potentially retain the same property methods without breaking the code that uses them – i.e. it's an extra layer of abstraction that protects you from breaking changes. A few years ago I might have bought that, but these days I only add in layers of abstraction when they're truly needed, so I will use the property for external access and the instance var directly for internal access (though I'll always assign to the property to ensure retain counts are correct). I can easily refactor to add in the abstraction if necessary at a later point, but if I don't need it now then I won't bother with it.
I tend to prefix my instance variables with ‘m_’, or just ‘m’ so I end up with ‘m_Name’ (I’m not a fan of underscores, but they’re very clear). This makes it nice and easy to see what’s a member of the class, and what’s local to the method I’m working in.
You should always access your instance variables via setters, getters or properties – only in init and dealloc should you do anything with your instance variables directly. My advice with properties and the dot syntax is that if you’re going to use it, stick with it for everything – don’t alternate between standard [self setBlah:…]; and self.blah = …;
You shouldn’t use “_” to prefix your instance variables since those are reserved for use by Apple. See: http://developer.apple.com/mac/library/documentation/Cocoa/Conceptual/ObjectiveC/Articles/ocLanguageSummary.html
Actually it’s just method names for which Apple reserves underscore prefixing. In my example, the instance variable has an underscore, but the synthesised accessor method does not, so this is OK.
Myself, I don’t write stuff like this:
– (id)initWithName:(NSString *)name
I use this instead:
– (id)initWithName:(NSString *)newName
avoids the compiler issue, saves a lot of (unnecessary) awkward _’s
I agree with Tony Arnold, only use instance variables directly in init or dealloc’s. The reason do it directly in init methods, is that at the point of initialization, sometimes an accessor method will have some kind of dependance on another instance variable that is not yet initialized (ex; for parameter checking, validation etc). So you need to get the object set up without having to run into those dependancies. Using instance variables directly elsewhere can cause you to inadvertently bypass internal logic though, so you don’t want to do that outside of init/dealloc
C# has nowhere near the elegance of obj-c imho. Invocations, Message Forwarding, KVO, Notifications etc. are extremely powerful features. I don’t see that in C#/.NET. Not to mention all the insane overloads…good grief. Talk about clunky. And the .NET frameworks look like a major ripoff of Cocoa. (The only horror I see in obj-c is the dot syntax!)