Impressions from my first six months of OS X development
Since I started my new job about 6 months ago, I’ve almost exclusively been programming in Objective-C + Cocoa for OS X (and to a minor extent iPhone). I’ve been thinking that I should summarize my first impressions of it somewhere so I’ll do it here.
The syntax
My first issue with the language was the syntax. It’s function calls look similar to the ones in LISP, even though the language is a lot closer to Java or C++. Writing function calls nested like
[[foo bar] haveFunWithStuff:stuff andMoreStuff:moreStuff];
instead of
ClassName.bar().haveFun(stuff, moreStuff);
is actually not that big of a deal. The main issue i have with it is that Objective-C methods become quite verbose, and Cocoa in particular has quite lengthy method names in it’s library.
The reason for using a somewhat weird syntax is pretty obvious though. Since Objective-C is a strict superset of C, it makes it apparent which code is “pure” C and which code isn’t. I assumed this is the reason for using the unusual string syntax @”foo bar” as well (to avoid parser collision with C style strings).
Dynamic typing
Since I’ve coded a bunch of python stuff I’m used to dynamic typing, but I don’t really think having it mixed with static typing like in Objective-C is that pretty. For an example, if you want to use the standard event bindings in Interface Builder, you might hook up one of your buttons to the message buttonDown on a controller in your application. The declaration of this method is.
- (IBAction)buttonDown:(id)sender;
This specifies that the sender can be an object of any type. Nothing can really be assumed about the sender, and any method can be called on it (however, if the actual instance doesn’t support the call an error will occur). The documentation for buttonDown can however state that sender should implement an informal protocol). Essentially this means that instead of forcing a formal protocol (like an Interface in Java), it assumes that the instance supports one or more methods. If it doesn’t then the application fails during runtime.
Cocoa also has support form formal protocols, which works like interfaces in Java. These look like
- doStuff:(id <MyProtocol>)foo;
and implicitly forces foo to implement the methods in MyProtocol. Dynamic typing IS useful, but I don’t really like having the protocols declared by the documentation and not the code.
Memory Management
Up until OS X 10.5, Objective-C applications for OS X didn’t support garbage collection. This meant that only manual memory management was supported, and this is handled through manual reference counting.
Essentially, every time an object instance foo becomes dependent on another object bar, foo is responsible for retaining bar. This is done by increasing its reference count by one like:
// Inside Foo.m
- setBar:(BarClass*)bar {
myBar = [bar retain];
}
Foo is also responsible for decreasing the reference count of bar when it’s done using it. This means that writing a setter for an object property can look like
- setBar:(BarClass*)bar {
// Check needed to avoid getting ref count to 0 if setting the same object twice in a row.
if (myBar != bar) {
[myBar release];
myBar = [bar retain];
}
}
This looks a bit messy, and it kind of is. I’m not sure if this method decreases the risk of using freed memory compared to just using malloc and free like in C, but if it does then it’s definitely worth it. I’ve had a bunch of memory related issues when playing around with iPhone programming, but this was mostly when I was completely fresh to the language so I can’t really blame its design.
The garbage collection provided in OS X > 10.5 however works quite well, and I use it for all my OS X code. Hopefully the iPhone will get it as well eventually, but until then you’re stuck with ref counting.
No Private/Public/Protected
Objective-C doesn’t have the concept of private and public methods. All methods defined in a class header is public to callers. It is however possible to write implementations or even new declarations (using categories) inside of the implementation files which leaves the methods unexposed. In practice, it’s still possible to call them. You will just be given a warning during compilaton.
This also means that you can’t write protected methods like in Java (only accessible for it’s class and subclasses). If you want to expose a method to subclasses, you’ll have to expose it to the whole world as well. This will make some classes APIs a bit messier, but it’s not a major issue.
No modules
Objective-C uses something quite close to C++ for class dependencies. Headers work line in C/C++ but instead of including them with #include, you use #import which removes the need to check if the header was already included.
This means that you will still run into cyclic imports like in C or C++ if you have two classes Foo and Bar with dependencies on each other. To solve this, you use a new kind of forward references for the classes, and move the #import call to the classes implementation files like this
// Foo.h
@class Bar;
@interface Foo : NSObject
{
Bar *bar;
}
@end
// Foo.m
#import "Bar.h"
@implementation Foo {
/* stuff */
}
@end
// Bar.h
@class Foo;
@interface Bar : NSObject
{
Foo *foo;
}
@end
// Bar.m
#import "Foo.h"
@implementation Bar {
/* stuff */
}
@end
I’d really prefer to have module system like in Java.
Categories
Categories are a way to monkey patch code into existing classes. I find it pretty nasty from the design perspective, since it pretty much completely ignore the concept of encapsulation.
// NSStringExtras.h
#import "NSString.h"
@interface NSString (extras)
- (void)sayHello; // Extends the Cocoa string class with a hello world method.
@end
They can be used in a few quite nice way though. One of them is declaring categories for a class inside it’s implementation. This makes it possible to declare methods which will appear private, which “fixes” the lack of privacy in Objective-C.
// Foo.h
@interface Foo : NSObject {
}
- (void)publicMethod;
@end
// Foo.m
@interface Foo (hidden)
- (void)privateMethod;
@end
@implementation Foo
- (void)publicMethod {
[self privateMethod];
}
@end
@implementation Foo (hidden)
- (void)privateMethod {
NSLog(@"Hello world");
}
@end
No abstract classes
You can’t make abstract classes in Objective-C. You can do hacks which makes it impossible to initialize the “abstract” base class, but the concept of abstract classes doesn’t exist . This is rather ugly.
Networking Programming with Cocoa
Cocoa doesn’t have a nice Socket class like in Java. You are instead given a few different options for networking including CFSocket (which is a C API), raw BSD sockets and NSSocketPort which is pretty poorly documented and lacks support for UDP.
The lack of a proper Socket class in the standard library is solved by asyncsocket though, which makes socket handling a lot less painful. Something like this should probably be in the standard library in my opinion.
Xcode and Interface Builder
Xcode is the IDE shipped with the OS X dev tools. It’s pretty decent, but has a few shortcomings. One of them is the revision control integration. Xcode has some support for it (CVS/SVN/Perforce), but it’s pretty lacking and it seems like a lot of people use external tools for their needs. If you just want to commit and fetch updates from a single branch then it’s probably good enough, but if you want to do more advanced stuff you’ll have to do it by some other means.
The unit-test integration is also quite lacking. Getting a test suite up and running is way more work than it should be, and I still have to do some work arounds to be able to use the debugger while running tests.
The debugger is otherwise quite nice. It wraps GDB and I haven’t had any major issues with it.
Interface Builder is the design tool used for laying out OS X/iPhone GUI’s and I actually really like it. The way it lets ju bind you GUI items to your application controller is easy to learn and quite powerful. It’s actually probably my favorite WYSIWYG editor for a compiled language.
KVC/KVO
Essentially built in support for the Observer pattern for all objects. Quite useful, even though it like most parts of Cocoa isn’t as strict as when utilizing the pattern in many other languages.
Testing
It seems to me like very few Cocoa developers seem to use unit tests. I’ve been trying to keep a reasonable test coverage, and the testing tools OCUnit and OCMock are rather decent. The backwards compatibility with C makes OCUnit a bit iffy at times, but other than that I think it’s ok.
Documentation
The Cocoa documentation is for the most part excellent, but surprisingly a bunch of stuff lacks online documentation completely (try searching for docs on the core audio matrix mixer for an example). Usually there’s some kind of example to get you started though.
The community
The online community for OS X development is pretty good. There are a few online resources such as cocoadev which contains tons of good data. The #macdev and #iphonedev IRC channels on freenode are also excellent (especially Psy|). Finally, the related apple mailing lists provides a lot of information about topics which aren’t really covered elsewhere.
If you are forced to use Core Audio for an example, be prepared to search through the mailing lists for help a lot.
Summary
I don’t really know what I think about Objective-C. It’s design is a lot less strict compared to C++ and Java, and I don’t know if I like it or not. It works just fine though and I can’t blame Apple for deciding to use it as the standard language to use for OS X dev stuff. It is however quite complex to get to know the correct way of doing stuff when using Cocoa. It took me quite some time until I fully understood the KVO mechanic which makes Cocoa bindings work and so on.
Something I know that I like though is Xcode. It has some shortcomings (mainly revision control), but it’s an awesome IDE which allows for very rapid development thanks to Interface Builder which is excellent.
If I’d have to summarize my opinion, I’d say that I’m cautiously positive to doing OS X development. I don’t hate it after six months, and that’s actually somewhat impressive.
Recent Comments