Objective-C syntax looks like line noise mixed with Klingon. But it’s concepts are not that far from standard programming practices, though some of the names of the concepts are unfamiliar because they were chosen before modern practices (like OOP) became common (and thus well understood with a shared syntax.) Some of it’s mechanisms are odd because it was designed to run as a preprocessor on top of C.
To start off, Objective-C is object oriented (sortof… in the same way “objective” is related to “object”).
A “class” in Obj-C has an interface (defined in a .h “header” file) and an implementation (defined in a .m “messages” file.)
Let’s take a look at an example. It’s time to get started on our adventure game, so (for now) create a console application and add a new class.
File -> New -> File -> OS X -> Cocoa -> Objective-C Class
Give your class a name and notice by default that it is a subclass of NSObject. NS, again stands for “NextStep” and the reason you have it is because Objective-C doesn’t have namespaces. So you’ll see a lot of capitalized prefix abbreviations.
You can see that two files were created. Jedi.h and Jedi.m. Click on Jedi.m and open the assistant editor to see Jedi.h alongside it. This is a handy feature in Xcode (when it works.)
@interface and @implementation are compiler directives. Each are followed by an @end directive. You can see that Jedi extends NSObject with the colon syntax similar to C#.
Jedi : NSObject
Let’s start by adding a property. Every Jedi is a person, and every person has a name. Create a Person class and change Jedi to extend person. Add an instance variable “name” to person.
//Person.h #import <Foundation/Foundation.h> @interface Person : NSObject { NSString * _name; } @property (readwrite) NSString *name; @end
// Person.m #import "Person.h" @implementation Person @synthesize name; @end
// Jedi.h #import <Foundation/Foundation.h> #import "Person.h" @interface Jedi : Person @end
This is our first real departure from familiar syntax. The compiler directives aren’t too scary and you can think of them like annotations for now.
The block after the class definition in Person.h defines instance variables. In our case, an NSString called name. All objects are pointers (yikes!) and NSString is no exception. NSString is an object that has all sorts of capabilities that a char * does not. Modern languages like Java and C# have String classes and Objective-C or rather it’s core library in Foundation.h from Cocoa (OpenStep) has NSString.
Don’t worry too much about pointers. Thanks to ARC, memory management is easy (ish) and the real hard part is understand the evolving memory management methods in Obj-C — including a lot of very good but recently outdated information on the internet. And code you are likely to see that was written as little as 1 year ago.
Moving on, a @property directive creates accessors for that instance variable. Kinda like attr_accessor in Ruby. But not quite. In the implementation, you also need to @synthesize the property in your implementation. This creates the getters and setters. With XCode 4.4+ you no longer need to add the @synthesize line. If a property is declared “readwrite” it will create both a getter and setter. If it is “readonly” it will only create a getter.
Let’s create a Jedi and welcome him to the adventure.
// main.m #import <Foundation/Foundation.h> #import "Jedi.h" int main(int argc, const char * argv[]) { @autoreleasepool { Jedi *jedi = [[Jedi alloc] init]; jedi.name = @"Luke"; NSLog(@"May the force be with you!, %@", jedi.name); } return 0; }
To instantiate our class we have to first allocate memory and the initialize our object. Thats what this line is doing:
Jedi *jedi = [[Jedi alloc] init]
Objective-C doesn’t have constructors. But you can nest calls. The square brackets [ ] are an indication that you’re dealing with objects. Here, you’re calling two methods that are built into NSObject — alloc, which wraps the C function malloc() and init which by convention initializes the object. This, if you will, is the standard constructor call — the equivalent to new in other languages like Java. And remember, every object is really a pointer.
Next we assign a name to our object. The dot notation syntax is familiar to us, but what it’s really doing is providing a little syntactic sugar for this message call.
[jedi setName: @"Skywalker"]
Now I’d better slow down a bit and explain what I mean by a message call. See, classes (and objects — instances of classes) don’t have methods in Objective-C, they have messages. For all practical purposes you can think of them as the same thing, and many people use the terms interchangeably. But it helps to remember what your doing inside the brackets is object oriented, and if you’ve used signals and slots in Qt you won’t be too blown away by the idea. There are a lot of ramifications for this, the main one being that you can dynamically send messages to objects (whether the message is supported or not — which could then trigger an exception) which allows for metaprogramming.
So you’re really sending a message to an instance of a class, not calling a method on an object. But for most purposes it amounts to the same thing. Even the latest Apple docs use the term “method”.
Dot notation is a node to those of us who are familiar with it from other languages, but it’s not universally popular with Objective-c purists.
But I’m getting ahead of myself in the example. Let’s get back to our code.
Every person has a name, and every Jedi is a person (assuming aliens are people too.) But what differentiates a Jedi from an ordinary person is the ability to use the force. Let’s add that capability by creating a method declaration in Jedi.h and an implementation in Jedi.m (very similar to what you’d expect in C) but with a slightly different syntax.
// Jedi.h #import <Foundation/Foundation.h> #import "Person.h" @interface Jedi : Person - (void)useTheForce; @end
The minus (-) symbol designates it as an instance method. If it were replaced with a plus (+) then it would be a class method — similar to using the “static” keyword in Java.
// Jedi.m #import "Jedi.h" @implementation Jedi - (void)useTheForce { NSLog(@"The Force is what gives a Jedi his power"); } @end
void is the return type. (Just pretend those parentheses weren’t there.) The semicolon is just like a function definition in a C header or declaration in a Java interface. (Note what we call interfaces are called protocols in Objective-C. The class declaration in a .h header file is called an “interface” in Objective-C. It’s easy to conflate the two if you’re not thinking about it.
And now let’s have our young Jedi try it out.
[jedi useTheForce]
Does the same thing as
jedi.useTheForce
but the latter gives us a warning “Property access result unused — getters should not be used for side effects.” Because the dot notation is syntactic sugar that expects it to be calling a property, not invoking a method (or calling a message)– which has side effects.