If you are familiar with object-oriented programming, then you'll find Obejctive-C classes to be an easy concept to grasp. Each language has a different way of handling objects and object classes, any particular language may not support all features, but the base concepts are all similar.
Structure
Here is a basic Objective-C class definition that you would expect to find in a header file:
@class OtherClass;
@interface SampleClassName : ParentClassName <ExampleProtocol, SecondProtocol>
{
NSInteger instanceVariable;
OtherClass *otherClassInstance;
}
@property (readwrite, retain) OtherClass *otherClassInstance;
+(id)sampleClass;
+(id)sampleClassWithOther:(OtherClass*)other byInteger:(NSInteger)integer;
-(id)init;
-(id)initWithOther:(OtherClass*)other byInteger:(NSInteger)integer;
-(void)dealloc;
-(NSString*)description;
@endThere's only about a score lines of text there, but there is a lot going on! Let's break it down.
The first line, @class OtherClass; is a class forward declaration. You won't use this often, but from time to time you'll need two classes to reference each other. That circular reference, try as you might, can't be given to the compiler from headers with the class definitions. The definition of either class references the other one the compiler hasn't seen yet, and returns a fatal compile error. The class forward declaration takes care of that. It does nothing more than tell the compiler, "There exists a class named OtherClass," which is enough for it to grok the class listed in this header file.
Next up in our header is the class definition. It declares the name of this class, SampleClassName, the name of its parent class ParentClassName and the list of protocols it supports ExampleProtocol and SecondProtocol.
Unlike C++, but like most other languages, Objective-C only supports single-inheritance.
Protocols
Protocols are like contracts, checked at compile time. A protocol is simply a list of functions, some required, some optional. When a class claims a Protocol in its header, it is claiming to provide all the required functions and as many of the optional ones as it needs.
Each of the functions in a protocol are callbacks that another class will call to obtain critical runtime information for a particular task. You'll use them when you're trying to display data or interact with a framework. For example, the UITableView uses methods defined in a protocol to get the data for each entry in the table. The Address Book framework's ABPeoplePickerNavigationController uses a protocol to let you know who was selected and notify you when it has been dismissed by the user.
If you've never used protocols before, it'll take a bit to get used to them. Once you do, you'll like them. While it is possible to manage table entries and dialog interactions without them, it usually requires subclassing the dialog box class or creating special objects in exactly the format the table required. Protocols make that unnecessary. You'll have fewer classes, cleaner, more normalized data flow and have more control of the program architecture.
We'll delve into protocols in more detail in a later post when we look at delegates.
Data & Properties
The next section in the anatomy of a class is the data declarations. That space between the squiggle-braces is 100% data definition. The syntax is just like any other data declaration in 'C', with the one caveat that class instance variables must always be pointers.
It is possible to define some scoping of class data in Objective-C. Like C++, Objective-C has three classifications, private, protected and public. Public variables can be touched by anyone. Protected variables can be touched by the defining class and any child classes. Private can only be touched by the defining class itself. By default, all data is "protected."
Now, if you looked at this as a 'C' programmer, you'd probably think the end squiggle ends the class definition. Not so. It is easier to think of that as the struct part of the class. Objective-C will append that struct to the struct sections from all the parent classes and some for internal class management to form the internal representation of the class data.
If the squiggle part defines what a class is, then the rest defines how the class acts.
In object-oriented programming, you shouldn't directly assign a variable in a class, but rather provide the data to the object itself and ask it to assign the variable. The same goes for asking an object for a variable. This convention makes it possible for class to have read-only variables and also provide computed data as though it were a variable. That is, a program may ask for an employees full name and the class will return the first name and surname concatenated. What this means is each variable a class intends to expose will have a setter and a getter, collectively called accessors. Properties take the grind out of writing the accessors.
In our example, the next item is a line that starts @property. Properties tell the compiler to autogenerate setters and getters with certain behaviors. In the code for our class, we'll see a @synthesize line for each property we define here where the compiler will create the actual code.
The behavior of the accessors is defined by the comma delimited list of items in the parenthesis.
The first behavior is access, readonly or readwrite. Unless specified, properties are readwrite by default.
The second is how the class deals with setting the variable. There are three different ways to set a variable. Yes, three. You can assign a variable, copy a variable or retain a variable. Even though assign is default, 99% of the time you will be using retain. The difference between the three has to do with memory management. This is a topic for a later blog post.
The third is nonatomic. By default, all the getters are atomic. There is some code added to the getter to ensure that the object still exists when the getter returns. The nonatomic keyword disables this and simply returns the value.
Finally, there are two keywords, getter= and setter=, that override the names of the getter and setter. By default, setters have the name setVariableName: and the getters are variableName. This is often used as getter=isFoo when the property Foo is a boolean.
Methods
Now we get to the functions. No, not functions. Methods.
Wait, what's the difference? Most of the time, almost nothing.
Objects in Objective-C are sent messages with code like [myObject aMessage:argument]; The 'C' code that is generated for a message is roughly obj_msgsend(myObject, aMessage, argument). This mechanism gives the classes more dynamic abilities when it comes to responding to messages. A class can respond to a message directly, in which case the end effect is very much like a function call. A class can also forward the message on to another object. Finally, a class can inspect the message and dynamically respond to it. Borrowing from Ruby on Rails, a class that manages database transactions could dynamically determine that [dbManager findByName:@"george"] means create and execute an SQL query searching for "george" in the "Name" column.
Because of the dynamic nature of methods, defining the methods in the class @interface is a bit more than a convenience for other programmers who will have to inherit your code, including future-you. The compiler will look at the list to validate the function exists and the proper number of arguments are used, but if the function is not listed or does not match the number and types of arguments, then the compiler will emit a warning, not an error. (For this reason, I strongly suggest enabling -Wall -Werror compiler options.)
The syntax for a method is completely different from a normal 'C' function. The first character is a plus or minus sign. Plus means this is a Class Method, a minus means this is an Instance Method.
Class methods are called directly on the class itself, not an instance of the class. While there is no requirement to do so, most class methods create new objects of the class type and initialize them. For example, the UIColor class has redColor, blueColor and other default class methods that create specific color objects. The two class methods that are listed in our sample class do exactly that: create and return an object of the class type.
Which brings us to the next bit of the declaration: the return type. The text inside the paragraph is the return type for the function. For our two class methods, these are id, the equivalent of void* for Objective-C objects.
If we were to look at our code, we'd see the two class methods do little more than create an instance of the class, call the matching init method, and return the object.
You may have noticed something peculiar about two of the methods. The initWithOther and matching class method sampleClassWithOther have variables inline with the method name. Objective-C does this to make code easier to read. The internal names of those two functions are sampleClassWithOther:byInteger: and initWithOther:byInteger:. The colon in the name indicates an argument, but is also a part of the full name. That means the methods foo and foo: are different methods. It is legal for your class to have both at the same time. These internal names are called selectors.
Summary
Wow, that was a lot. We've talked about classes, protocols, properties, messages, class and instance methods, the id type and selectors. I'm going to take a moment in the next blog post to talk a bit about conventions, and then we'll get on to the code.
First posted on Sentai Digital.