Thursday, July 14, 2005

Cocoa

When I originally created this blog I named it after Apple's Frameworks for creating Mac applications, "Cocoa". At the time, I was working my way through Aaron Hillegass's book Cocoa Programming for Mac OS X. Since Apple gives away its development tools for free with their OS I had previously played around with it. But for one reason or another I found myself leaving OS X development and instead investigating technologies more closely related to Java (the language I use at work). I also recently spent a couple months looking at C# and .NET. But finally I'm back to looking at Cocoa and think I'm ready to compare Cocoa to Java and C#. Like my C# review so many months ago (February 2005) I'll try to comment on the things that make Cocoa different from Java.

But first of all, some history. Cocoa development is done in a language called Objective C (objc for short). It was invented in 1983 by Brad Cox at Stepstone. It is described as a thin layer on top of C so unsurprisingly, anything you can do in C you can do in Objective C. The so called "thin layer" adds Smalltalk-like syntax to add object-oriented features. Objective-C uses the open-source GCC (GNU Compiler Collection) and so people can program in Objective C on any platform. I've even written a small command line program on Windows just to prove that it can be done.

Secondly, Mac OS X's (and Cocoa's) heritage is NextStep. After Steve Jobs left Apple he founded NeXT computer in 1985 and in many ways created a computer that was ahead of its time. Part of that vision, often overshadowed by the media's attention to the hardware, was the object oriented application development environment called NextStep, based on Objective-C. This set of libraries (or frameworks) was ported to several platforms (Intel, PA-RISC, SPARC, etc.) during its lifetime and became known as OpenStep in a deal with Sun MicroSystems. When Apple eventually bought Next in 1996, the NextStep frameworks became known as Cocoa. To this day the classes in the framework all begin with the abbreviation NS. Interestingly, part of Next's work in the early 90's to make NextStep portable (specifically to Intel) is what is enabling Apple to switch the Macintosh's processor from PowerPC to Intel with such little trouble.

Namespaces and File structure
  • By convention, the interface is placed in a header file (*.h) and the implementation is placed in a file with the "m" extension (*.m). This violates the spirit of the DRY principle (Don't Repeat Yourself) because the method names are now in two places. But on the upside the class's logical public interface is easily communicated through the header file. It also gives you a way of protecting your source code while still allowing third parties to use your frameworks. Java and C# both suffer from being easily decompiled.
  • There are no namespaces in ObjC. Instead a naming convention suggests using an abbreviation at the beginning of each class name (e.g., all the Apple class names begin with "NS").
Misc
  • Like Java and all other C-based languages other than C#, Cocoa uses camelCase instead of PascalCase.
  • ObjC frequently uses the at-symbol (i.e., @) to differentiate ObjC functionality from standard C functionality.
  • Object references look like C pointers. (see next bullet point)
  • NSString is one of the most commonly used classes in Cocoa. Like Java, it stores immutable Unicode strings. Use the @-symbol in front of a C string to create an NSString instance. (e.g., NSString *name = @"Cocoa";)
  • C's printf String formatting functionality is included with NSString's factory method stringWithFormat:. The NSLog() function for printing to console also supports printf-type functionality. (BTW: Java finally got around to supporting this with Java SE 5.)
  • Like C#, ObjC uses a colon (:) for subclassing instead of Java's extends keyword
  • ObjC also uses the colon (:) for implementing an interface instead of Java's implements keyword.
  • Class methods are denoted with the plus-symbol (+) instead of Java's static keyword.
  • Instance methods are denoted with the minus-symbol (-).
  • Visibility identifiers are nearly the same as Java's except they use an @-symbol in front (i.e., @public, @protected, @private). And like Java, the default is @protected when it's not explicitly defined. The biggest difference is that methods are always public. Private-like methods are created by excluding them from the class's @interface.
  • Exception handling is the same in ObjC as in Java with the keywords @try, @catch, @finally, @throw except exceptions are not part of the method signature (like C#). Exceptions were new in Mac OS X 10.3 (Panther) so I doubt that they are utilized all that frequently.
  • Like Java, ObjC uses an #import keyword to include references to the classes that it uses.
  • Like Java, the Cocoa framework is a singly rooted class hierarchy starting with NSObject.
  • An interface in Java is known as a protocol in ObjC
  • instead of Java's instanceof, NSObject has several methods for accomplishing similar functionality, isKindOfClass:, isMemberOfClass:, conformsToProtocol:
Method Syntax
  • ObjC methods define labels for each parameter. For example, an instance method for a Person class might look like this:
    • - (Person *)initWithFirstName:(NSString *)first Last:(NSString *)last;
    • This method returns a Person instance and takes two NSString parameters.
    • The first parameter is described by "initWithFirstName:".
    • The second parameter has the label "Last:".
    • The full method name is: "-initWithFirstName:Last:"
  • Using Smalltalk-like messaging syntax you would cause this method to be invoked with something like this:
    • [person initWithFirstName:@"Bob" Last:@"Builder"];
    • More generically [object methodName:param1 Label2:param2];
    • The nice thing about this calling syntax is that it's easy to tell from the calling side what each parameter is for whereas in traditional C, Java, and C# you need some documentation to help out.
    • Like Java, each method call is dynamically looked up and invoked.
  • ObjC's object oriented features are based on Smalltalk. As a result, the idea of invoking an object's method is better phrased as "sending a message to an object". In ObjC it is completely valid to send a message to an object that cannot respond to it and you will receive no error. This stands in stark contrast to Java's strong type checking at compile time. When coming from a Java background this seems strangely odd, but as you become accustomed to this dynamic behavior you soon begin to recognize its power.
  • There is a special ObjC type called id that is a pointer to an object of any class. By judicious use of id and ObjC's dynamic message mechanism you can avoid a lot of typecasting that is so prevalent in Java code. Naturally you can (and should) use the variables and parameters of the types you are expecting but in some cases where the type cannot be known ahead of time the id type is very valuable and produces succinct easy to understand code.
Memory Management
  • To create an instance of an object use alloc (e.g., [Person alloc];)
  • ObjC's constructors are responsible for initializing instance variables. The default constructor is init. Constructors that take parameters use init as a prefix.
  • Typically alloc and init are called together (e.g., [[Person alloc] init];
  • ObjC does not include any kind of automatic memory management. There is no garbage collector. Instead Cocoa uses reference counting. You use the instance methods retain and release to increment and decrement the reference count. When the count reaches zero the object is freed.
  • The alloc method automatically increments the reference count by one.
  • ObjC's destructor is dealloc. It is responsible for releasing instance variables that point to objects.
  • There is a second instance method that you can use to express your intention to release an object called autorelease.
    • GUI Cocoa application automatically instantiate NSAutoReleasePool.
    • It is this object that becomes responsible for actually releasing your object at some point in the future.
    • This autorelease mechanism allows you to return new objects from a method and is frequently employed by class factory methods (e.g., [NSString stringWithString:@"This will create an autoreleased copy"];)
    • If the calling method does not retain the returned object then it will be automatically released.
    • It is the event loop in a GUI application that creates and destroys release pools.
  • In practice this memory management scheme is more error-prone than languages with garbage collection and requires diligence to implement properly. However, memory releases happen relatively frequently and the application's memory footprint is more predictable and manageable.
Inheritance
  • Use super to refer to superclass methods or variables.
  • Use self to refer to the instance's method or variables.
  • Constructors should call the super constructor first. (e.g., -(Person *) init{if (self = [super init]){/*initialize variables*/} return self;}
  • Destructors should call the super dealloc last (e.g., -(void)dealloc{/*free instance variables*/[super dealloc];}
Properties
  • ObjC is similar to Java in using accessor and mutator methods to get and set private instance variables. The only difference is that the get methods are named the same as the instance variable.
  • I prefer C#'s and Delphi's ability to declare properties as first-class citizens. I believe it makes the code easier to read.
  • Java didn't include operator overloading because it typically gets abused in practice. ObjC doesn't include it either but having seen C#'s and Ruby's implementations I think if used properly it could make code more readable (I say "could" because I haven't used either extensively and hesitate to say with certainty)
Selectors
  • When using Java's Swing components (a.k.a., GUI Controls) you typically register an event listener with each component to be informed when the user interacts with it (e.g., clicks on a button). Because each listener must conform to a specific interface you end up creating a lot of anonymous inner classes. The code's intention is lost in the overly-verbose mechanics. Even if the IDE is generating this stuff for you, it's still ugly.
  • ObjC's selector specifies the signature of a message and is not bound to any particular class.
  • Create one using @selector( selectorName )
  • Or use NSSelectorFromString(@"selectorName:") to create one from a string.
  • You can programatically invoke a selector using one of the performSelector:, performSelector:withObject: variants in NSObject.
  • InerfaceBuilder, the tool for creating user interfaces for Cocoa applications, uses selectors in the target/action paradigm to graphically wire visual controls to methods that look like - (IBAction) methodName:(id)sender; with absolutely no code.
Collections
  • Cocoa has NSArray, NSSet, and NSDictionary and their mutable counterparts, NSMutableArray, NSMutableSet, and NSMutableDictionary which are functionaly similiar to Java's List, Set, and Map interfaces and various implementations.
  • Cocoa uses NSEnumerator to traverse a collection's members which is similar to Java Iterator.
  • Cocoa doesn't have any of the syntactic sugar of Java or C#, such as Java's enhanced for loop but because of ObjC's dynamic messaging infrastructure the need for typecasting isn't nearly as onerous so perhaps it's not needed.
Categories
  • An interesting feature of ObjC called categories, is its ability to dynamically add methods to existing classes even if you don't have the source code for those classes. Now when you think "Why didn't they add a method that does blah blah blah", you can just add it yourself!
  • mixins provide a similar feature in Ruby.
  • NSObject also has a method called poseAsClass: which allows a subclass to stand in for its superclass. This subclass can override superclass methods and add new methods.
Summary

ObjC has no garbage collector and doesn't generate bytecode which are two pretty big features of Java and C# but regardless it has just about everything that those other languages have plus more. It is a good solid object oriented time-tested application framework and unlike Microsoft with its .NET framework, Apple "eats its own dogfood". It uses Cocoa extensively to build its applications (e.g., GarageBand, iPhoto, iCal, iChat, Mail, etc). I've been fairly happy with the toolset although I wish refactoring support was available in XCode (which is also at least partially built with Cocoa). Next step is to go into building a "real" application, stay tuned...

No comments: