Objective-C Succinctly: Methods

In this chapter, we'll explore Objective-C methods in much more detail than we have in previous chapters. This includes an in-depth discussion of instance methods, class methods, important built-in methods, inheritance, naming conventions, and common design patterns.


Instance vs. Class Methods

We've been working with both instance and class methods throughout this book, but let's take a moment to formalize the two major categories of methods in Objective-C:

  • Instance methods - Functions bound to an object. Instance methods are the "verbs" associated with an object.
  • Class methods - Functions bound to the class itself. They cannot be used by instances of the class. These are similar to static methods in C#.

As we've seen many times, instance methods are denoted by a hyphen before the method name, whereas class methods are prefixed with a plus sign. For example, let's take a simplified version of our Person.h file:

Likewise, the corresponding implementation methods also need to be preceded by a hyphen or a plus sign. So, a minimal Person.m might look something like:

The sayHello method can be called by instances of the Person class, whereas the personWithName method can only be called by the class itself:

Most of this should be familiar to you by now, but now we have the opportunity to talk about some of the unique conventions in Objective-C.


The Super Keyword

In any object-oriented environment, it's important to be able to access methods from the parent class. Objective-C uses a very similar scheme to C#, except instead of base, it uses the super keyword. For example, the following implementation of sayHello would display HELLO in the output panel, and then call the parent class' version of sayHello:

Unlike in C#, override methods do not need to be explicitly marked as such. You'll see this with both the init and dealloc methods discussed in the following section. Even though these are defined on the NSObject class, the compiler doesn't complain when you create your own init and dealloc methods in subclasses.


Initialization Methods

Initialization methods are required for all objects-a newly allocated object is not considered "ready to use" until one of its initialization methods has been called. They are the place to set defaults for instance variables and otherwise set up the state of the object. The NSObject class defines a default init method that doesn't do anything, but it's often useful to create your own. For example, a custom init implementation for our Ship class could assign default values to an instance variable called _ammo:

This is the canonical way to define a custom init method. The self keyword is the equivalent of C#'s this-it's used to refer to the instance calling the method, which makes it possible for an object to send messages to itself. As you can see, all init methods are required to return the instance. This is what makes it possible to use the [[Ship alloc] init] syntax to assign the instance to a variable. Also notice that because the NSObject interface declares the init method, there is no need to add an init declaration to Ship.h.

While simple init methods like the one shown in the previous sample are useful for setting default instance variable values, it's often more convenient to pass parameters to an initialization method:

If you're coming from a C# background, you might be uncomfortable with the initWithAmmo method name. You'd probably expect to see the Ammo parameter separated from the actual method name like void init(uint ammo); however, Objective-C method naming is based on an entirely different philosophy.

Recall that Objective-C's goal is to force an API to be as descriptive as possible, ensuring that there is absolutely no confusion as to what a method call is going to do. You can't think of a method as a separate entity from its parameters-they are a single unit. This design decision is actually reflected in Objective-C's implementation, which makes no distinction between a method and its parameters. Internally, a method name is actually the concatenated parameter list.

For example, consider the following three method declarations. Note that the second and third are not built-in methods of NSObject, so you do need to add them to the class' interface before implementing them.

While this looks like method overloading, it's technically not. These are not variations on the init method-they are all completely independent methods with distinct method names. The names of these methods are as follows:

This is the reason you see notation like indexOfObjectWithOptions:passingTest: and indexOfObjectAtIndexes:options:passingTest: for referring to methods in the official Objective-C documentation (taken from NSArray).

From a practical standpoint, this means that the first parameter of your methods should always be described by the "primary" method name. Ambiguous methods like the following are generally frowned upon by Objective-C programmers:

Instead, you should use a preposition to include the first parameter in the method name, like so:

Including both OtherShip and aShip in the method definition may seem redundant, but remember that the aShip argument is only used internally. Someone calling the method is going to write something like shootOtherShip:discoveryOne, where discoveryOne is the variable containing the ship you want to shoot. This is exactly the kind of verbosity that Objective-C developers strive for.

Class Initialization

In addition to the init method for initializing instances, Objective-C also provides a way to set up classes. Before calling any class methods or instantiating any objects, the Objective-C runtime calls the initialize class method of the class in question. This gives you the opportunity to define any static variables before anyone uses the class. One of the most common use cases for this is to set up singletons:

Before the first time [Ship sharedShip] is called, the runtime will call [Ship initialize], which makes sure the singleton is defined. The static variable modifier serves the same purpose as it does in C#-it creates a class-level variable instead of an instance variable. The initialize method is only called once, but it's called on all super classes, so you have to take care not to initialize class-level variables multiple times. This is why we included the self == [Ship class] conditional to make sure _shareShip is only allocated in the Ship class.

Also note that inside of a class method, the self keyword refers to the class itself, not an instance. So, [self alloc] in the last example is the equivalent of [Ship alloc].


Deallocation Methods

The logical counterpart to an instance's initialization method is the dealloc method. This method is called on an object when its reference count reaches zero and its underlying memory is about to be deallocated.

Deallocation in MMR

If you're using manual memory management (not recommended), you need to release any instance variables that your object allocated in the dealloc method. If you don't free instance variables before your object goes out of scope, you'll have dangling pointers to your instance variables, which means leaked memory whenever an instance of the class is released. For example, if our Ship class allocated a variable called _gun in its init method, you would have to release it in dealloc. This is demonstrated in the following example (Gun.h contains an empty interface that simply defines the Gun class):

You can see the dealloc method in action by creating a Ship and releasing it, like so:

This also demonstrates how auto-released objects work. The dealloc method won't be called until the end of the @autoreleasepool block, so the previous code should output the following:

Note that the first NSLog() message in main() is displayed before the one in the dealloc method, even though it was called after the autorelease call.

Deallocation in ARC

However, if you're using automatic reference counting, all of your instance variables will be deallocated automatically, and [super dealloc] will be called for you as well (you should never call it explicitly). So, the only thing you have to worry about are non-object variables like buffers created with C's malloc().

Like init, you don't have to implement a dealloc method if your object doesn't need any special handling before it is released. This is often the case for automatic reference-counting environments.


Private Methods

A big hurdle for C# developers transitioning to Objective-C is the apparent lack of private methods. Unlike C#, all methods in an Objective-C class are accessible to third parties; however, it is possible to emulate the behavior of private methods.

Remember that clients only import the interface of a class (i.e. the header files)-they should never see the underlying implementation. So, by adding new methods inside of the implementation file without including them in the interface, we can effectively hide methods from other objects. Albeit, this is more convention-based than "true" private methods, but it's essentially the same functionality: trying to call a method that's not declared in an interface will result in a compiler error.

Figure 25 Attempting to call a private method

Attempting to call a "private" method

For example, let's say you needed to add a private prepareToShoot method to the Ship class. All you have to do is omit it from Ship.h while adding it to Ship.m:

This declares a public method called shoot, which will use the private prepareToShoot method. The corresponding implementation might look something like:

As of Xcode 4.3, you can define private methods anywhere in the implementation. If you use the private method before the compiler has seen it (as in the previous example), the compiler checks the rest of the implementation block for the method definition. Prior to Xcode 4.3, you had to either define a private method before it was used elsewhere in the file, or forward-declare it with a class extension.

Class extensions are a special case of categories, which are presented in the upcoming chapter. Just as there is no way to mark a method as private, there is no way to mark a method as protected; however, as we'll see in the next chapter, categories provide a powerful alternative to protected methods.


Selectors

Selectors are Objective-C's way of representing methods. They let you dynamically "select" one of an object's methods, which can be used to refer to a method at run time, pass a method to another function, and figure out whether an object has a particular method. For practical purposes, you can think of a selector as an alternative name for a method.

Figure 26 Developers representation of a method vs Objective-Cs representation

Developers' representation of a method vs. Objective-C's representation

Internally, Objective-C uses a unique number to identify each method name that your program uses. For instance, a method called sayHello might translate to 4984331082. This identifier is called a selector, and it is a much more efficient way for the compiler to refer to methods than their full string representation. It's important to understand that a selector only represents the method name-not a specific method implementation. In other words, a sayHello method defined by the Person class has the same selector as a sayHello method defined by the Ship class.

The three main tools for working with selectors are:

  • @selector() - Return the selector associated with a source-code method name.
  • NSSelectorFromString() - Return the selector associated with the string representation of a method name. This function makes it possible to define the method name at run time, but it is less efficient than @selector().
  • NSStringFromSelector() - Return the string representation of a method name from a selector.

As you can see, there are three ways to represent a method name in Objective-C: as source code, as a string, or as a selector. These conversion functions are shown graphically in the following figure:

Figure 27 Converting between source code strings and selectors

Converting between source code, strings, and selectors

Selectors are stored in a special data type called SEL. The following snippet demonstrates the basic usage of the three conversion functions shown in the previous figure:

First, we use the @selector() directive to figure out the selector for a method called sayHello, which is a source-code representation of a method name. Note that you can pass any method name to @selector() -it doesn't have to exist elsewhere in your program. Next, we use the NSStringFromSelector() function to convert the selector back into a string so we can display it in the output panel. Finally, the conditional shows that selectors have a one-to-one correspondence with method names, regardless of whether you find them through hard-coded method names or strings.

Method Names and Selectors

The previous example uses a simple method that takes no parameters, but it's important to be able to pass around methods that do accept parameters. Recall that a method name consists of the primary method name concatenated with all of the parameter names. For example, a method with the signature

would have a method name of:

This is what you would pass to @selector() or NSSelectorFromString() to return the identifier for that method. Selectors only work with method names (not signatures), so there is not a one-to-one correspondence between selectors and signatures. As a result, the method name in the last example will also match a signature with different data types, including the following:

The verbosity of Objective-C's naming conventions avoids most confusing situations; however, selectors for one-parameter methods can still be tricky because appending a colon to the method name actually changes it into a completely different method. For example, in the following sample, the first method name doesn't take a parameter, while the second one does:

Again, naming conventions go a long way toward eliminating confusion, but you still need to make sure you know when it's necessary to add a colon to the end of a method name. This is a common issue if you're new to selectors, and it can be hard to debug, as a trailing colon still creates a perfectly valid method name.

Performing Selectors

Of course, recording a selector in a SEL variable is relatively useless without the ability to execute it later on. Since a selector is merely a method name (not an implementation), it always needs to be paired with an object before you can call it. The NSObject class defines a performSelector: method for this very purpose.

This is the equivalent of calling sayHello directly on joe:

For methods with one or two parameters, you can use the related performSelector:withObject: and performSelector:withObject:withObject: methods. The following method implementation:

could be called dynamically by passing the aPerson argument to the performSelector:withObject: method, as demonstrated here:

This is the equivalent of passing the parameter directly to the method:

Likewise, the performSelector:withObject:withObject: method lets you pass two parameters to the target method. The only caveat with these is that all parameters and the return value of the method must be objects-they don't work with primitive C data types like int, float, etc. If you do need this functionality, you can either box the primitive type in one of Objective-C's many wrapper classes (e.g., NSNumber) or use the NSInvocation object to encapsulate a complete method call.

Checking for the Existence of Selectors

It's not possible to perform a selector on an object that hasn't defined the associated method. But unlike static method calls, it's not possible to determine at compile time whether performSelector: will raise an error. Instead, you have to check if an object can respond to a selector at run time using the aptly named respondsToSelector: method. It simply returns YES or NO depending on whether the object can perform the selector:

If your selectors are being dynamically generated (e.g., if methodToCall is selected from a list of options) or you don't have control over the target object (e.g., joe can be one of several different types of objects), it's important to run this check before trying to call performSelector:.

Using Selectors

The whole idea behind selectors is to be able to pass around methods just like you pass around objects. This can be used, for example, to dynamically define an "action" for a Person object to execute later on in the program. For example, consider the following interface:

Included code sample: Selectors

Along with the corresponding implementation:

As you can see, calling the coerceFriend method will force a different object to perform some arbitrary action. This lets you configure a friendship and a behavior early on in your program and wait for a particular event to occur before triggering the action:

This is almost exactly how user-interface components in iOS are implemented. For example, if you had a button, you would configure it with a target object (e.g., friend), and an action (e.g., action). Then, when the user eventually presses the button, it can use performSelector: to execute the desired method on the appropriate object. Allowing both the object and the method to vary independently affords significant flexibility-the button could literally perform any action with any object without altering the button's class in any way. This also forms the basis of the Target-Action design pattern, which is heavily relied upon in the iOS Succinctly companion book.


Summary

In this chapter, we covered instance and class methods, along with some of the most important built-in methods. We worked closely with selectors, which are a way to refer to method names as either source code or strings. We also briefly previewed the Target-Action design pattern, which is an integral aspect of iOS and OS X programming.

The next chapter discusses an alternative way to create private and protected methods in Objective-C.

This lesson represents a chapter from Objective-C Succinctly, a free eBook from the team at Syncfusion.
Tags:

Comments

Related Articles