This hour’s lesson marks the midpoint in our exploration of the Apple iOS development platform. It will give us a chance to sit back, catch our breath, and get a better idea of what it means to “code” for iOS. Both OS X and iOS share a common development environment and a common development language: Objective-C.
Objective-C provides the syntax and structure for creating applications on Apple platforms. For many, learning Objective-C can be daunting, but with patience, it may quickly become the favorite choice for any development project. This hour takes you through the steps you need to know to be comfortable with Objective-C and starts you down the path to mastering this unique and powerful language.
To better understand the scope of this hour, take a few minutes to search for Objective-C or object-oriented programming in your favorite online bookstore. You will find quite a few books—lengthy books—on these topics. In this book, roughly 30 pages cover what other books teach in hundreds of pages. Although it’s not possible to fully cover Objective-C and object-oriented development in this single hour, we can make sure that you understand enough to develop fairly complex apps.
To provide you with the information you need to be successful in iOS development, this hour concentrates on fundamentals—the core concepts that are used repeatedly throughout the examples and tutorials in this book. The approach in this hour is to introduce you to a programming topic in general terms and then look at how it will be performed when you sit down to write your application. Before we begin, let’s look a bit closer at Objective-C and object-oriented programming.
Most people have an idea of what programming is and have even written a simple program. Everything from setting your TiVo to record a show to configuring a cooking cycle for your microwave is a type of programming. You use data (such as times) and instructions (like “record”) to tell your devices to complete a specific task. This certainly is a long way from developing for iOS, but in a way the biggest difference is in the amount of data you can provide and manipulate and the number of different instructions available to you.
There are two primary development paradigms. First, imperative programming (a subset of which is called procedural programming) implements a sequence of commands that should be performed. The application follows the sequence and carries out activities as directed. Although there might be branches in the sequence or movement back and forth between some of the steps, the flow is from a starting condition to an ending condition, with all the logic to make things work sitting in the middle.
The problem with imperative programming is that it lends itself to growing, without structure, into an amorphous blob. Applications gain features when developers tack on bits of code here and there. Often, instructions that implement a piece of functionality are repeated over and over wherever something needs to take place. Procedural programming refers to an imperative programming structure that attempts to avoid repetition by creating functions (or procedures) that can be reused. This works to some extent, but long-term still often results in code bloat. The benefit of this approach, however, is that it is quite easy to pick up and learn: You create a series of instructions, and the computer follows them.
The other development approach, and what we use in this book, is object-oriented programming (OOP). OOP uses the same types of instructions as imperative development but structures them in a way that makes your applications easy to maintain and promotes code reuse whenever possible. In OOP, you create objects that hold the data that describes something along with the instructions to manipulate that data. Perhaps an example is in order.
Consider a program that enables you to track reminders. With each reminder, you want to store information about the event that will be taking place—a name, a time to sound an alarm, a location, and any additional miscellaneous notes that you may want to store. In addition, you need to be able to reschedule a reminder’s alarm time or completely cancel an alarm.
In the imperative approach, you have to write the steps necessary to track all the reminders, all the data in the reminders, check every reminder to see whether an alarm should sound, and so on. It’s certainly possible, but just trying to wrap your mind around everything that the application needs to do could cause some serious headaches. An object-oriented approach brings some sanity to the situation.
In an object-oriented model, you could implement a reminder as a single object. The reminder object would know how to store the properties such as the name, location, and so on. It would implement just enough functionality to sound its own alarm and reschedule or cancel its alarm. Writing the code, in fact, would be very similar to writing an imperative program that only has to manage a single reminder. By encapsulating this functionality into an object, however, we can then create multiple copies of the object within an application and have them each fully capable of handling separate reminders. No fuss and no messy code!
Most of the tutorials in this book make use of one or two objects, so don’t worry about being overwhelmed with OOP. You’ll see enough to get accustomed to the idea, but we’re not going to go overboard.
Another important facet of OOP is inheritance. Suppose that you want to create a special type of reminder for birthdays that includes a list of birthday presents that a person has requested. Instead of tacking this onto the reminder object, you could create an entirely new “birthday reminder” that inherits all the features and properties of a reminder and then adds in the list of presents and anything else specific to birthdays.
OOP brings with it a whole range of terminology that you need to get accustomed to seeing in this book (and in Apple’s documentation). The more familiar you are with these terms, the easier it will be to look for solutions to problems and interact with other developers. Let’s establish some basic vocabulary now:
setAlarm
to set the alarm for a given reminder. Methods are public or private, depending on whether they can be accessed only within the class where they are defined (private), or within other classes as well (public).countReminders
that provides a count of all the reminder objects that have been created. If you’re familiar with other OO languages, you may recognize this as a static method.setAlarm
method, you would presumably need to include the time to set. The time, in this case, would be a parameter used with the setAlarm
method.self
to refer to the object.Tip
You might be wondering: If almost everything in iOS development is a subclass of something else, is there some sort of master class that “starts” this tree of inheritance? The answer is yes. The NSObject
class serves as the starting point for most of the classes you’ll be using in iOS. This isn’t something you really need to worry about in the book—just a piece of trivia to think about.
It’s important to know that when you develop for iOS you’re going to be taking advantage of hundreds of classes that Apple has already written for you. Everything from creating onscreen buttons to manipulating dates and writing files is covered by prebuilt classes. You’ll occasionally want to customize some of the functionality in those classes, but you’ll be starting out with a toolbar already overflowing with functionality.
Confused? Don’t worry! This book introduces these concepts slowly, and you’ll quickly get a feel for how they apply to your projects as you work through several tutorials in the upcoming hours.
A few years ago, I would have answered this question with “one of the strangest-looking languages I’ve ever seen.” Today, I love it (and so will you). Objective-C was created in the 1980s and is an extension of the C language. It adds many additional features to C and, most important, an OOP structure. Objective-C is primarily used for developing OS X and iOS applications and has attracted a devoted group of followers who appreciate its capabilities and syntax.
Objective-C statements are easier to read than other programming languages and often can be deciphered just by looking at them. For example, consider the following line that compares whether the contents of a variable called myName
is equal to John:
[myName isEqualToString:@"John"]
It doesn’t take a very large mental leap to see what is going on in the code snippet. In traditional C, this might be written as follows:
strcmp(myName,"John")
The C statement is a bit shorter, but it does little to convey what the code is actually doing.
Because Objective-C is implemented as a layer on top of C, it is still fully compatible with code that is written entirely in C. For the most part, this isn’t something that you should concern yourself with, but unfortunately, Apple has left a bit of “cruft” in its iOS software development kit (SDK) that relies on C-language syntax. You’ll not encounter this often, and it isn’t difficult to code with when it does occur, but it does take away from the elegance of Objective-C just a little.
Caution: Case Counts
Objective-C is case sensitive. If a program is failing, make sure that you aren’t mixing case somewhere in the code.
Now that you have an idea of what OOP and Objective-C are, let’s take a look at how you’ll be using them over the course of this book.
In the preceding hour, you learned how to use Xcode to create projects and navigate their files. As mentioned then, the vast majority of your time will be spent in the project group of Xcode, which is shown for the MyNewApp project in Figure 3.1. You’ll be adding methods to class files that Xcode creates for you when you start a project or, occasionally, creating your own class files to implement entirely new functionality in your application.
Okay, sounds simple enough, but where will the coding take place? If you create a new project look, you’ll see quite a few different files staring back at you.
Creating a class creates two different files: an interface (or header) file (.h extension) and an implementation file (.m extension). The interface file is used to define a list of all of the methods and properties that your class will make available to other classes. This is useful for other pieces of code to determine how to access information and features in your class; if your code isn’t sharing information with other classes, chances are your interface file will be mostly empty.
Let’s review the structure of the very short, and entirely made-up, interface file in Listing 3.1.
1: #import <UIKit/UIKit.h>
2:
3: @interface myClass : myParent <myProtocol> {
4: NSString *_myString;
5: IBOutlet UILabel *_myLabel;
6: }
7:
8: @property (weak, nonatomic) NSString *myString;
9: @property (weak, nonatomic) NSString *myOtherString;
10: @property (weak, nonatomic) IBOutlet UILabel *myOtherLabel;
11:
12: +(NSString)myClassMethod:(NSString *)aString;
13:
14: -(NSDate)myInstanceMethod:(NSString *)aString anotherParam:(NSURL *)aURL;
15:
16: -(IBAction)myActionMethod:(id)sender;
17:
18: @end
Caution: Line Numbers Are for Reference Only!
Throughout the book, I present code samples like this one. Often, they include line numbers so that I can easily reference the code and explain how it works. Objective-C does not require line numbers, nor will the code work if you leave them in your application. If you see a line prefixed with a number and a colon (#:), don’t type the line number prefix!
1: #import <UIKit/UIKit.h>
First, in line 1, the interface file uses the #import
directive to include any other interface files that our application will need to access. The string <UIKit/UIKit.h>
designates the specific file (in this case, UIKit, a system framework which gives us access to a vast majority of the classes).
New in Xcode 5, you can use a variation of #import
—the module @import
directive. Unlike #import
, which usually requires you to add additional files to your project, a single @import
statement takes care of everything. The only drawback is that frameworks you’re using must support modules for this to work. Because the iOS system frameworks do, you should be in good shape using this feature, unless you are using code from a third party.
The syntax for importing modules is @import <module name>;
. (Note the need for the trailing semicolon.)
For example, line 1 could be rewritten as follows:
1: @import UIKit;
Importing modules dramatically speeds compilation of your application and automatically links appropriate frameworks into your app as needed. Most of Apple’s examples still use the older-style #import
statement, but I suspect they’ll migrate to @import
over time.
Whenever we need to import something, I explain how and why in the text. The UIKit example is included by default when Xcode sets up your classes and covers most of what you need for this book’s examples.
Note
Wait a sec, what’s a directive? Directives are commands that are added to your files that help Xcode and its associated tools build your application. They don’t implement the logic that makes your app work, but they are necessary for providing information on how your applications are structured so that Xcode knows how to deal with them.
Line 3 uses the @interface
directive to begin a set of lines (enclosed in {} braces) that can describe the instance variables that your class will be providing:
3: @interface myClass : myParent <myProtocol> {
4: NSString *_myString;
5: IBOutlet UILabel *_myLabel;
6: }
In this example, a variable that contains an object of type NSString
named _myString
is declared, along with an object of type UILabel
that will be referenced by the variable _myLabel
. An additional keyword IBOutlet
is added to the front of the UILabel
declaration to indicate that this is a user interface object that will be defined in Interface Builder (IB). You learn more about IBOutlet
in Hour 5, “Exploring Interface Builder.”
Notice that the instance variables I’ve used in this example start with an underscore (_
) character. This is a naming convention used in Objective-C to denote instance variables (versus other variables, such as a counter, that you might use in a portion of your code). You can create instance variables without underscores, but when using Xcode’s code-generation tools, it will follow this approach.
One very, very important thing to note is that this is not the only way to define instance variables (or outlets). It is certainly a legitimate way to do so, but not necessarily the most efficient. In fact, in much of your development, you won’t even need to create instance variables. Instead, you’ll implicitly create your instance variables by defining properties. We get to this approach in the next section.
Caution: Yes, the Semicolon Is Required
All instance variables, method declaration lines, module imports, and property declarations must end with a semicolon (;
).
Notice that line 3 includes a few additional items after the @interface
directive: myClass : myParent <myProtocol>
. The first of these is the name that we’re giving the class that we’re working on. Here, we’ve decided the class will be called myClass
. The class name is then followed by a colon (:
) and a list of the classes that this class is inheriting from (that is, the parent classes). Finally, the parent classes are followed by a list of protocols enclosed within angle brackets, <>.
Tip
The implementation and interface files for a class will usually share the name of the class. Here, the interface file would be named myClass.h and the implementation file myClass.m.
Note
Protocol? What’s a protocol? Protocols are a feature of Objective-C that sound complicated but really aren’t. Sometimes you will come across features that require you to write methods to support their use, such as providing a list of items to be displayed in a table. The methods that you need to write are grouped together under a common name; this is known as a protocol.
Some protocol methods are required, and others are optional; it just depends on the features you need. A class that implements a protocol is said to conform to that protocol.
One of the most critical, and multifunctional, directives you will use is @property
, demonstrated in lines 8–10:
8: @property (weak, nonatomic) NSString *myString;
9: @property (weak, nonatomic) NSString *myOtherString;
10: @property (weak, nonatomic) IBOutlet UILabel *myOtherLabel;
The @property
directive is used to simplify how you interact with the instance variables that you’ve defined in your interface—and, as you’ll learn shortly, can even act as a shortcut for creating the instance variables themselves. In essence, defining a property provides a layer of abstraction on top of an instance variable. Instead of interacting with a variable directly (and potentially in ways you shouldn’t), you use a property instead. Line 8, for example, defines a property that we can use to access the instance variable defined previously in line 4.
Traditionally, to interact with the contents of your instance variables, you have to use (and write!) methods called getters and setters (or accessors and mutators, if you want to sound a bit more exotic). These methods, as their names suggest, are created to get and set values stored in your instance variable objects without touching the actual variables themselves.
What @property
does is write the getter and setter for us and give us a really nice way of using them. The getter is simply the name of the property, while the setter is the property name (capitalized) with the prefix set. For example, to set the instance variable _myString
through a property, you could use the following:
[myClassObject setMyString:@"Hello World"];
And to retrieve value from _myString
, you might type this:
theStringInMyObject=[myClassObject myString];
Not too tough, but it’s not as easy as it could be. Using @property
also allows us to read and write instance variable values just by typing the name of the object that contains the property, followed by a period, and the name of the property. This is called dot notation:
myClassObject.myString=@"Hello World";
theStringInMyObject=myClassObject.mystring;
We make use of this feature nearly everywhere that we need easy access to instance variables. After we’ve given the “property” treatment to an instance variable (line 8), we almost always refer to the property rather than the variable itself. That leads us to two final points that can lead to a ton of confusion.
First, because properties and instance variables are so closely related, Objective-C makes it possible to implicitly declare an instance variable just by declaring the property. In other words, consider line 9:
9: @property (weak, nonatomic) NSString *myOtherString;
This single line is all that is actually needed to declare a new instance variable _myOtherString
and an associated property named myOtherString
; no explicit declaration of the instance variable is required.
In addition, property declarations can be used to create a property, instance variable, and an IBOutlet
, all in one, as shown in line 10:
10: @property (weak, nonatomic) IBOutlet UILabel *myOtherLabel;
You’ll encounter this often in Apple’s iOS project templates: a property declaration that implicitly defines an instance variable. We’ll use this approach ourselves because writing a single line of code is easier (and less prone to errors) than writing several lines to do the same thing.
The second extremely important point in this discussion is understanding the relationship between the names of properties and instance variables. As previously mentioned, instance variables typically begin with an underscore (_
). The same cannot be said for properties. Properties don’t begin with an underscore, but refer to instance variables that do. If you declare an instance variable named myVariable
and want to create a property that references it, you can’t do it by creating a property named myVariable
. Doing so creates a new instance variable with, you guessed it, an underscore at the start: _myVariable
. Although there are ways to change this naming convention, it’s best to stick with a standard. We will be using the @property
directive to create both our properties and their corresponding instance variables (just as in lines 9 and 10). Whatever we name a property, just keep in mind that in the background, a variable with almost the same name (but starting with an _) is automatically created.
Tip
The attributes (weak
, nonatomic
) that are provided to the @property
directive tell Xcode how to treat the object the property refers to. The first, weak
, informs the system that the object it is referring to can be cleaned up from memory when it isn’t being used anymore. The second, nonatomic
, instructs Xcode that it doesn’t need to worry about different parts of the application using the property at the same time. These are the attributes you’ll use in nearly all circumstances, so get used to them.
As you’ll see later in the book, sometimes we need to use the attribute strong
in place of weak
. The weak
attribute lets Xcode be more efficient in cleaning up objects in memory. It also avoids what is called a circular reference, where an object can’t be removed from memory, because it points to another object that, in turn, points back to it. Unfortunately, sometimes the system may be a bit overzealous in its desire to keep things clean for us, and strong
needs to be employed. A strong
reference means that the object will be kept in memory until we explicitly tell the system to remove it. It’s pretty rare that we need to worry about this, but I’ll point it out when it’s a concern.
Note
It’s generally a bad idea to access instance variables that have a corresponding property directly, but there are cases where you may need to use them instead of properties. If I recall correctly, there is literally one occurrence of this within this entire book.
To be complete, let’s quickly see how you reference a variable versus a property.
To use an instance variable, you just access it by name. If you are writing a class with an instance variable defined as _myVariable
, anywhere you type _myVariable
you are accessing the variable directly.
Properties, however, work quite a bit differently.
To retrieve a property from an object, you write <objectname>.<propertyname>
to access it. That means if there is a property myProperty
in an object myAmazingObject
, you type myAmazingObject.myProperty
.
That’s fine, but what if you want to access the property from inside the class where it is defined? Simple. You do exactly the same thing, but use self
to refer to the object, as in self.<propertyname>
. If you were to use just the property name by itself, Xcode would assume you are trying to access an instance variable and throw an error.
This will all be obvious once you start coding. You’ll be using self.<propertyname>
in every hour, so you have plenty of time to get used to the syntax.
The final piece of the interface file are method definitions, known as prototypes. Lines 12, 14, and 16 declare three methods that will be implemented in the class:
12: +(NSString)myClassMethod:(NSString *)aString;
13:
14: -(NSDate)myInstanceMethod:(NSString *)aString anotherParam:(NSURL *)aURL;
15:
16: -(IBAction)myActionMethod:(id)sender;
Prototypes follow a simple structure. They begin with a +
or -
. The +
denotes a class method, and -
indicates an instance method. Next, the type of information the method returns is provided in parentheses, followed by the name of the method itself. If the method takes a parameter, the name is followed by a colon, the type of information the method is expecting, and the variable name that the method will use to refer to that information. If multiple parameters are needed, a short descriptive label is added, followed by another colon, data type, and variable name. This pattern can repeat for as many parameters as needed.
In the example file, line 12 defines a class method named myClassMethod
that returns an NSString
object and accepts an NSString
object as a parameter. The input parameter is made available in a variable called aString
.
Line 14 defines an instance method named myInstanceMethod
that returns an NSDate
object, also takes an NSString
as a parameter, and includes a second parameter of the type NSURL
that will be available to the method via the variable aURL
.
The third instance method, myActionMethod
declared in line 16 is different from the others because it defines an action. The return type is set to IBAction
—a special keyword that indicates that this is a method that will be triggered via a connection created in IB. In other words, methods that use IBAction
as their return type will be called when the user touches a button, presses a switch, or otherwise interacts with your application’s UI. These methods take a single parameter, usually denoted as sender
that is set to whatever object the user interacted with. Just as for the IBOutlet
mentioned earlier, you’ll be learning much more about IBAction
in Hour 5.
Note
You learn more about NSString
, NSDate
, and NSURL
in Hour 4, “Inside Cocoa Touch,” but as you might guess, these are objects for storing and manipulating strings, dates, and URLs, respectively.
Tip
You will often see methods that accept or return objects of the type id
. This is a special type in Objective-C that can reference any kind of object and proves useful if you don’t know exactly what you’ll be passing to a method or if you want to be able to return different types of objects from a single method.
Another popular return type for methods is void
. When you see void
used, it means that the method returns nothing.
To end the interface file, add @end
on its own line. You can see this on line 18 of the example file:
18: @end
That’s it for the interface. Although that might seem like quite a bit to digest, it covers almost everything you’ll see in an interface/header file.
The implementation file (.m extension) holds all the “stuff” that makes your class work along with definitions of private properties, instance variables, and methods. Let’s take a look at Listing 3.2, a sample implementation file myClass.m that corresponds to the interface file we’ve been reviewing.
1: #import "myClass.h"
2:
3: @interface myClass () {
4: NSString *_myPrivateString;
5: }
6: @property (weak, nonatomic) IBOutlet UILabel *myPrivateLabel;
7: -(IBAction)myPrivateActionMethod:(id)sender;
8: @end
9:
10: @implementation myClass
11:
12: +(NSString)myClassMethod:(NSString *)aString {
13: // Implement the Class Method Here!
14: }
15:
16: -(NSString)myInstanceMethod:(NSString *)aString anotherParam:(NSURL *)aURL {
17: // Implement the Instance Method Here!
18: }
19:
20: -(IBAction)myPrivateActionMethod:(id)sender {
21: // Implement the Private Method Here!
22: }
23: @end
The #import
directive kicks things off in line 1 by importing the interface file associated with the class:
1: #import "myClass.h"
When you create your projects and classes in Xcode, this is automatically added to the code for you. If any additional interface files need to be imported, you should add them to the top of your interface file rather than here.
When we get to lines 3–8, we run into something that looks a bit familiar: another @interface
block with a property declaration (myPrivateLabel
). The code serves nearly the same purpose as everything we saw in the interface (.h) file, but with a distinct difference.
You can put instance variables, properties, outlets, and method definitions in this block, but the items that you put here are considered private to your class. In other words, if another application wants to use your class, it cannot directly access the items you add in this block; they are available only to you within the class itself (appropriate for internal calculations, counters, error handling, and the such). This is known, in Objective-C, as an empty category.
Note
Although the interface (.h) file can contain outlets and actions (and traditionally has been used for this purpose), this is considered “bad form” in Xcode 5. In all but a few cases, all of our outlets and actions will be defined in the implementation (.m) files.
The @implementation
directive, shown in line 10, tells Xcode what class the file is going to be implementing. In this case, the file should contain the code to implement myClass:
10: @implementation myClass
To provide an area to write your code, the implementation file must restate the method definitions, but, rather than ending them with a semicolon (;), a set of curly braces, {}, is added at the end, as shown in lines 12–14, 16–18, and 20–22. All the magic of your programming takes place between these braces:
12: +(NSString)myClassMethod:(NSString *)aString {
13: // Implement the Class Method Here!
14: }
15:
16: -(NSString)myInstanceMethod:(NSString *)aString anotherParam:(NSURL *)aURL {
17: // Implement the Instance Method Here!
18: }
19:
20: -(IBAction)myPrivateActionMethod:(id)sender {
21: // Implement the Private Method Here!
22: }
Notice that even these represent a class method, a public instance method, and a private method; the coding all takes place in the implementation file. Whatever you’ve declared as public in your interface file, and private at the top of the implementation file, must be implemented here. (I’ve left out the implementation of the public method myActionMethod
to save space.)
Tip
You can add a text comment on any line within your class files by prefixing the line with the //
characters. If you want to create a comment that spans multiple lines, you can begin the comment with the characters /*
and end with */
.
To end the implementation file, add @end
on its own line, just as with the interface file. Line 23 of the example shows this:
23: @end
Even though we’ve just spent quite a bit of time going through the structure of the interface and implementation files, you’re rarely (if ever) going to need to type it all out by hand. Whenever you add a new class to your Xcode project, the structure of the file will be set up for you; the @interface
and @implementation
directives and overall file structure will be in place before you write a single line of code. What’s more, much of the work of declaring properties, instances, variables, and methods can be done visually. Of course, you still need to know how to write code manually, but Xcode 4.x goes a long way toward making sure you don’t have to sweat the details.
Note
The following is information that will be helpful in your journey into iOS development. We won’t specifically be writing categories or protocols, but you may encounter sample code that use these features.
In addition to writing classes, you can define functionality through Objective-C categories and protocols. A category enables you to add methods to a class without subclassing or override a class’s existing methods. This means that you could, for example, add new methods to built-in classes, like NSString
.
A protocol does not implement functionality, but defines what another class may or must implement to comply with the protocol. In other words, you can define a protocol that enables other classes to interface with your code, which is often done with objects like the UITable
, which expects certain methods to exist in your code so as to provide it with data. For Java developers, this is similar to the idea of an interface.
To define a category, you just decide what class your category is going to add methods to and what your category is going to be named (presumably based on the features it provides), and then you create a new interface file using this format:
@interface ClassNameToUpdate (CategoryName)
// Declarations for new (or overridden) methods go here
@end
The implementation file follows:
@implementation ClassNameToUpdate (CategoryName)
// Implementations of new methods go here
@end
The naming conventions for category files are the class name you want to update plus (+
) the name of the category (for example, ClassNameToUpdate+CategoryName.h and ClassNameTo Update+CategoryName.m).
After you have created your category, you can import the interface file into your other classes and code away. Your application will behave as if the original class now includes the methods you wrote.
Caution: No Instance Variables for You!
Note that you can override methods with a category, but you cannot add new instance variables or properties.
Creating a new protocol is easier than creating a class or a category because you’re just defining functionality to be implemented elsewhere. To define a new protocol, create a new interface file with what you want to name your protocol. The contents of the file should follow this format:
@protocol ANewProtocol <NSObject>
@optional
// Declarations for optional methods
@required
// Declarations for required methods
@end
The <NSObject>
denotes the base protocol that this protocol will be based on (similar to inheriting from a class). Methods that must be implemented to conform to the protocol are found under the @required
directive, and optional ones are under the @optional
directive.
Note
Methods defined without @optional
or @required
preceding them are assumed to be required.
We’ve explored the notion of classes, methods, and instance variables, but you probably still don’t have a real idea of how to go about making a program do something. So, this section reviews several key programming tasks that you’ll be using to implement your methods:
Earlier we documented what instance variables and properties in your interface and implementation files will look like, but we didn’t really get into the process of how you declare (or define) them (or use them). Properties/instance variables store information that is available across all the methods in your class—but they’re not really appropriate for small temporary storage tasks, such as formatting a line of text to output to a user—for that we’ll use just plain, everyday variables. These are only available in the method in which they are declared, and are destroyed automatically when the method is done.
Most commonly, you’ll be declaring several variables at the start of your methods, using them for various calculations, and then getting rid of them when you’ve finished with them.
Whatever the purpose, you declare your variables using this syntax:
<Type> <Variable Name>;
The type is either a primitive data type or the name of a class that you want to instantiate and use.
Primitive data types are defined in the C language and are used to hold very basic values. Common types you’ll encounter include the following:
For example, to declare an integer variable that will hold a user’s age, you might enter the following:
int userAge;
After a primitive data type is declared, the variable can be used for assignments and mathematical operations. The following code, for example, declares two variables, userAge
and userAgeInDays
, and then assigns a value to one and calculates the other:
int userAge;
int userAgeInDays;
userAge=30;
userAgeInDays=userAge*365;
Pretty easy, don’t you think? Primitive data types, however, will make up only a very small number of the variables types that you use. Most variables you declare are used to store objects.
Just about everything that you’ll be working with in your iOS applications will be an object. Text strings, for example, will be instances of the class NSString
. Buttons that you display on the screen are objects of the class UIButton
. You’ll learn about several of the common data types in the next hour’s lesson. Apple has literally provided hundreds of different classes that you can use to store and manipulate data.
Unfortunately for us, for a computer to work with an object, it can’t just store it like a primitive data type. Objects have associated instance variables and methods, making them far more complex. To declare a variable as an object of a specific class, we must declare the variable as a pointer to an object. A pointer references the place in memory where the object is stored, rather than a value. To declare a variable as a pointer, prefix the name of the variable with an asterisk. For example, to declare a variable of type NSString
with the intention of holding a user’s name, we might type this:
NSString *userName;
Once declared, you can use the variable without the asterisk. It is only used in the declaration to identify the variable as a pointer to the object.
Tip
When a variable is a pointer to an object, it is said to reference or point to the object. This is in contrast to a variable of a primitive data type, which is said to store the data. Pointers, although largely concealed in basic Cocoa development, are an important topic to understand. For a quick introduction, watch these videos: http://www.youtube.com/watch?v=h-HBipu_1P0 or http://www.youtube.com/watch?v=7-EppTJK7WQ.
Even after a variable has been declared as a pointer to an object, it still isn’t ready to be used. Xcode, at this point, only knows what object you intend the variable to reference. Before the object actually exists, you must manually prepare the memory it will use and perform any initial setup required. This is handled via the processes of allocation and initialization, which we review next.
Before an object can be used, memory must be allocated and the contents of the object initialized. This is handled by sending an alloc
message to the class that you’re going to be using, followed by an init
message to what is returned by alloc
. The traditional syntax used in Objective-C has been this:
[[<class name> alloc] init];
But we can simplify that a bit by just using the new
message, which does the same thing as calling alloc
then init:
[<class name> new];
For example, to declare and create a new instance of the UILabel
class, you could use the following code:
UILabel *myLabel;
myLabel=[UILabel new];
Once allocated and initialized, the object is ready to use.
Note
We haven’t covered the method messaging syntax in Objective-C, but we do so shortly. For now, it’s just important to know the pattern for creating objects.
When we initialized the UILabel
instance, we did create a usable object, but it doesn’t yet have any of the additional information that makes it useful. Properties such as what the label should say or where it should be shown on the screen have yet to be set. We would need to use several of the object’s other methods to really make use of the object.
These configuration steps are sometimes a necessary evil, but Apple’s classes often provide a special initialization method called a convenience method. These methods can be invoked to set up an object with a basic set of properties so that it can be used almost immediately.
For example, the NSURL
class, which you use later to work with web addresses, defines a convenience method called initWithString
.
To declare and initialize an NSURL
object that points to the website http://www.teachyourselfios.com, we might type the following:
NSURL *iOSURL;
iOSURL=[[NSURL alloc] initWithString:@"http://www.teachyourselfios.com/"];
Without any additional work, we’ve allocated and initialized a URL with an actual web address in a single line of code.
Tip
In this example, we actually created another object, too: an NSString
. By typing the @
symbol followed by characters in quotes, you allocate and initialize a string. This feature exists because strings are so commonly used that having to allocate and initialize them each time you need one would make development quite cumbersome.
In your adventures in Objective-C, you will encounter instances where a method you are implementing has a generic reference to an object but you can’t work with it because Xcode doesn’t know the exact type of object it is. In these cases, you must use typecasting (or simply casting) to force Xcode to recognize the object.
For example, imagine that you are implementing a method that receives, as one of its parameters, an object of the type id
named unknownObject
. We know, however, that the only object that would be passed to the method is an instance of a class called anImportantClass
. The id
type can refer to anything, so until we tell Xcode what type of object it is, we can’t access its properties or methods.
To typecast a variable so that it is recognized as another type, place the type inside of parentheses in front of the variable:
(anImportantClass *)unknownObject
Using this, we can assign the unknownObject
to a new variable of the correct type:
anImportantClass *myKnownObject=(anImportantClass *)unknownObject;
After the cast variable is assigned to an object of the correct type, we can interact with it directly as that type. We can also choose to just typecast it whenever we need it. We can even use typecasting to access properties within an object.
If the anImportantClass
defined a property called anImportantProperty
, we could access that by including an extra set of parentheses around the object we’re casting:
((anImportantClass *)unknownObject).anImportantProperty
This looks a bit unusual, I know, but it will come in very handy later in the book. It’s easier to understand when you see it in an actual application; so for the moment, just be aware that it is an available development tool.
You’ve already seen the methods used to allocate and initialize objects, but this is only a tiny picture of the methods you’ll be using in your apps. Let’s start by reviewing the syntax of methods and messaging.
To send an object a message, give the name of the variable that is referencing the object followed by the name of the method—all within square brackets. If you’re using a class method, just provide the name of the class rather than a variable name:
[<object variable or class name> <method name>];
Things start to look a little more complicated when the method has parameters. A single parameter method call looks like this:
[<object variable> <method name>:<parameter value>];
Multiple parameters look even more bizarre:
[<object variable> <method name>:<parameter value>
additionalParameter:<parameter value>];
An actual example of using a multiple parameter method looks like this:
[userName compare:@"John" options:NSCaseInsensitive];
Here an object userName
(presumably an NSString
) uses the compare:options
method to compare itself to the string "John"
in a non-case-sensitive manner. The result of this particular method is a Boolean value (true
or false
), which could be used as part of an expression to make a decision in your application. (We review expressions and decision making next.)
Note
Throughout the lessons, methods are referred to by name. If the name includes a colon (:), this indicates a required parameter. This is a convention that Apple has used in its documentation and that has been adopted for this book.
Tip
A useful predefined value in Objective-C is nil
. The nil
value indicates a lack of any value at all. You’ll use nil
in some methods that call for a parameter that you don’t have available. A method that receives nil
in place of an object can actually pass messages to nil
without creating an error—nil
simply returns another nil
as the result.
This is used a few times later in the book and should give you a clearer picture of why this behavior is something we’d actually want to happen.
Something that you’ll see when looking at Objective-C code is that the result of a method is sometimes used directly as a parameter within another method. In some cases, if the result of a method is an object, a developer sends a message directly to that result.
In both of these cases, using the results directly avoids the need to create a variable to hold the results. Want an example that puts all of this together? We’ve got one for you.
Assume you have two NSString
variables, userFirstName
and userLastName
, that you want to capitalize and concatenate, storing the results in another NSString
called finalString
. To keep this simple, we’ll assume there is already a space concatenated onto the userFirstName
variable.
The NSString
instance method capitalizedString
returns a capitalized string, and stringByAppendingString
takes a second string as a parameter and concatenates it onto the string invoking the message. Putting this together (disregarding the variable declarations), the code looks like this:
tempCapitalizedFirstName=[userFirstName capitalizedString];
tempCapitalizedSecondName=[userLastName capitalizedString];
finalString=[tempCapitalizedFirstName
stringByAppendingString:tempCapitalizedSecondName];
Instead of using these temporary variables, however, you could just substitute the method calls into a single combined line:
finalString=[[userFirstName capitalizedString]
stringByAppendingString:[userLastName capitalizedString]];
This can be a powerful way to structure your code, but it can also lead to long and rather confusing statements. Do what makes you comfortable; both approaches are equally valid and have the same outcome.
Tip
A confession. I have a difficult time referring to using a method as sending a “message to an object.” Although this is the preferred terminology for OOP, all we’re really doing is executing an object’s method by providing the name of the object and the name of the method.
Although most of your coding will be within methods, you will also encounter “blocks” when using the iOS frameworks. Sometimes referred to as handler blocks in the Xcode documentation, these are chunks of code that can be passed as values when calling a method. They provide instructions that the method should run when reacting to a certain event.
For example, imagine a personInformation
object with a method called setDisplayName
that would define a format for showing a person’s name. Instead of just showing the name, however, setDisplayName
might use a block to let you define, programmatically, how the name should be shown:
[personInformation setDisplayName:^(NSString firstName, NSString lastName)
{
// Implement code here to modify the first name and last name
// and display it however you want.
}];
Interesting, isn’t it? Blocks are relatively new to iOS development and are used sparingly in this book. When you start developing motion-sensitive apps, for example, you will pass a block to a method to describe what to do when a motion occurs.
Where blocks are used, we walk through the process. To learn more about these strange and unusual creatures, read Apple’s A Short Practical Guide to Blocks in the Xcode documentation.
For an application to react to user input and process information, it must be capable of making decisions. Every decision in an app boils down to a yes or no result based on evaluating a set of tests. These can be as simple as comparing two values, to something as complex as checking the results of a complicated mathematical calculation. The combination of tests used to make a decision is called an expression.
If you recall your high school algebra, you’ll be right at home with expressions. An expression can combine arithmetic, comparison, and logical operations.
A simple numeric comparison checking to see whether a variable userAge
is greater than 30 could be written as follows:
userAge>30
When working with objects, we need to use properties within the object and values returned from methods to create expressions. To check to see whether a string stored in an object userName
is equal to "John"
, we could use this:
[userName compare:@"John"]
Expressions are not limited to the evaluation of a single condition. We could easily combine the previous two expressions to find a user who is over 30 and named John:
userAge>30 && [userName compare:@"John"]
As mentioned repeatedly, you’re going to be spending lots of time working with complex objects and using the methods within the objects. You cannot make direct comparisons between objects as you can with simple primitive data types. To successfully create expressions for the myriad objects you’ll be using, you must review each object’s methods and properties.
Typically, depending on the outcome of the evaluated expression, different code statements are executed. The most common way of defining these different execution paths is with an if-then-else
statement:
if (<expression>) {
// do this, the expression is true.
} else {
// the expression isn't true, do this instead!
}
For example, consider the comparison we used earlier to check a userName NSString
variable to see whether its contents were set to a specific name. If we want to react to that comparison, we might write the following:
if ([userName compare:@"John"]) {
userMessage=@"I like your name";
} else {
userMessage=@"Your name isn't John, but I still like it!";
}
Another approach to implementing different code paths when there are potentially many different outcomes to an expression is to use a switch
statement. A switch
statement checks a variable for a value and then executes different blocks of code depending on the value that is found:
switch (<numeric value>) {
case <numeric option 1>:
// The value matches this option
break;
case <numeric option 2>:
// The value matches this option
break;
default:
// None of the options match the number.
}
Applying this to a situation where we might want to check a user’s age (stored in userAge
) for some key milestones and then set an appropriate userMessage
string if they are found, the result might look like this:
switch (userAge) {
case 18:
userMessage=@"Congratulations, you're an adult!";
break;
case 21:
userMessage=@"Congratulations, you can drink champagne!";
break;
case 50:
userMessage=@"You're half a century old!";
break;
default:
userMessage=@"Sorry, there's nothing special about your age.";
}
In some situations, you will need to repeat several instructions over and over in your code. Instead of typing the lines repeatedly, you can loop over them. A loop defines the start and end of several lines of code. As long as the loop is running, the program executes the lines from top to bottom and then restarts again from the top. The loops you’ll use are of two types: count based and condition based.
In a count-based loop, the statements are repeated a certain number of times. In a condition-based loop, an expression determines whether a loop should occur.
The count-based loop you’ll be using is called a for
loop and consists of this syntax:
for (<initialization>;<test condition>;<count update>) {
// Do this, over and over!
}
The three “unknowns” in the for
statement syntax are a statement to initialize a counter to track the number of times the loop has executed, a condition to check to see whether the loop should continue, and finally, an increment for the counter. A loop that uses the integer variable count
to loop 50 times could be written as follows:
int count;
for (count=0;count<50;count=count+1) {
// Do this, 50 times!
}
The for
loop starts by setting the count
variable to 0
. The loop then starts and continues as long as the condition of count<50
remains true
. When the loop hits the bottom curly brace (}
) and starts over, the increment operation is carried out and count
is increased by 1
.
Note
In C and C-like languages, like Objective-C, integers are usually incremented by using ++
at the end of the variable name. In other words, rather than using count=count+1
, most often you’ll encounter count++
, which does the same thing. Decrementing works the same way, but with --
.
In a condition-based loop, the loop continues while an expression remains true. There are two variables of this loop type that you’ll encounter, while
and do-while:
while (<expression>) {
// Do this, over and over, while the expression is true!
}
and
do {
// Do this, over and over, while the expression is true!
} while (<expression>);
The only difference between these two loops is when the expression is evaluated. In a standard while
loop, the check is done at the beginning of the loop. In the do-while
loop, however, the expression is evaluated at the end of every loop. In practice, this difference ensures that in a do-while
loop, the code block is executed at least once; a while
loop may not execute the block at all.
For example, suppose that you are asking users to input their names and you want to keep prompting them until they type John. You might format a do-while
loop like this:
do {
// Get the user's input in this part of the loop
} while (![userName compare:@"John"]);
The assumption is that the name is stored in a string object called userName
. Because you wouldn’t have requested the user’s input when the loop first starts, you would use a do-while
loop to put the test condition at the end. Also, the value returned by the string compare
method has to been negated with the !
operator because you want to continue looping as long as the comparison of the userName
to "John"
isn’t true
.
Loops are a very useful part of programming and, along with the decision statements, will form the basis for structuring the code within your object methods. They allow code to branch and extend beyond a linear flow.
Although an all-encompassing picture of programming is beyond the scope of this book, this should give you some sense of what to expect in the rest of the book. Let’s now close out the hour with a topic that causes quite a bit of confusion for beginning developers: memory management.
In the first hour of this book, you learned a bit about the limitations of iOS devices as a platform. One of the biggies, unfortunately, is the amount of memory that your programs have available to them. Because of this, you must be extremely judicious in how you manage memory. If you’re writing an app that browses an online recipe database, for example, you shouldn’t allocate memory for every single recipe as soon as your application starts.
Each time you allocate memory for an object, you’re using up memory on your iOS device. If you allocate too many objects, you run out of memory, and your application crashes or is forced to quit. To avoid a memory problem, keep objects around long enough to use them only, and then get rid of them.
If you’ve read previous editions of this book, or even browsed online iOS source code, chances are you’ve encountered the retain
and release
messages (many, many times). These messages, when passed to an object, indicate that the object is needed, or is no longer being used (release
).
Behind the scenes, the iOS maintains a “retain” count to determine when it can get rid of an object. For example, when an object is first allocated, the retain count is incremented. Any use of the retain
message on the object also increases the count.
The release
message, in contrast, decrements the count. As long as the retain count remains above 0, the object is not removed from memory. When the count reaches 0, the object is considered unused and is removed.
In your applications, you previously needed to add release
code for any object you allocated. Think about that on a large scale: applications with hundreds or thousands of objects, needing to be manually retained or released. If you missed one, memory leaks and application crashes occurred! If you released an object too soon, more application crashes occurred.
Consider the earlier example of allocating an instance of NSURL:
NSURL *iOSURL;
iOSURL=[[NSURL alloc] initWithString:@"http://www.teachyourselfios.com/"];
Suppose that after you allocate and initialize the URL, you use it to load a web page. After the page loads and you’re done with the object, you must manually tell Xcode that you no longer have a need for it by writing the following:
[iOSURL release];
Thankfully, releasing and retaining objects is a thing of the past, courtesy of Automatic Reference Counting.
In the latest Xcode releases, Apple has implemented a new compiler called LLVM, along with a feature known as Automatic Reference Counting (ARC). ARC uses a powerful code analyzer to look at how your objects are allocated and used, and then it automatically retains and releases them as needed. When nothing is referencing an object, ARC ensures it is automatically removed from memory. No more retain
or release
messages to be sent, no more phantom crashes and memory leaks; you just code and it works.
All new projects that you build in Xcode, including those in this book, will automatically take advantage of ARC. In fact, ARC is so good at what it does that it will not let you write applications that include release
, retain
, dealloc
, or autorelease
. So, how does this translate into your development process? Just as you would hope: You write the code you need, initialize and use objects when you want, and when they are no longer referenced by anything else, the memory they occupied is automatically freed.
For most objects you declare and use in a method, you do not need to do anything—when the method is finished, there are no more references to the object and it is automatically freed. The same goes for instance variables/properties you’ve declared with the weak
attribute. Of course, it’s hyperbole to say that errors won’t happen with ARC; we have to use strong
in a few places in this book to keep iOS from deciding that we have finished with an object before we actually do.
When the strong
attribute has been used, you need to tell Xcode that you’re finished using an object if you want it removed from memory. How do you do that? Easy: by setting its reference to nil.
For a property named myObject
, you write the following:
self.myObject=nil;
If you’ve done any Objective-C development before ARC, you’ll quickly appreciate how little time you now need to spend on memory management. If you’re new to Objective-C, just relax and enjoy!
You’ve learned quite a bit in this hour’s lesson, and there are plenty of places for even the most experienced developer to make mistakes. As with everything, practice makes perfect, and you’ll have plenty of opportunities for applying what you’ve learned in the book’s tutorials.
Keep in mind that a typical book would spend multiple chapters on these topics, so our goal has been to give you a starting point that future hours will build on, not to define everything you’ll ever need to know about Objective-C and OOP.
Although you can be successful in learning iOS programming without spending hours and hours learning more Objective-C, you will find it easier to create complex applications if you become more comfortable with the language. Objective-C, as mentioned before, is not something that can be described in a single hour. It has a far-reaching feature set that makes it a powerful and elegant development platform.
To learn more about Objective-C, check out Programming in Objective-C 2.0, Third Edition (Addison-Wesley Professional, 2011), Objective-C Phrasebook (Addison-Wesley Professional, 2011), and Xcode 4 Unleashed (Sams, 2012).
Of course, Apple has its own Objective-C documentation that you can access directly from within the Xcode documentation tool. (You learn more about this in the next hour.) I recommend the following documents provided by Apple:
You can read these within Xcode or via the online Apple iOS Reference Library at http://developer.apple.com/library/ios/. One quick warning: These documents are several hundred pages long, so you might want to continue your Objective-C education in parallel with the lessons in this book.
In this hour, you learned about object-oriented development and the Objective-C language. Objective-C will form the structure of your applications and give you tools to collect and react to user input and other changes. After reading this hour’s lesson, you should understand how to make classes, instantiate objects, call methods, and use decision and looping statements to create code that implements more complex logic than a simple top-to-bottom workflow. You should also have an understanding of memory management and how it works under the Xcode ARC system.
Much of the functionality that you’ll be using in your applications will come from the hundreds of built-in classes that Apple provides within iOS, which we delve into in Hour 4.
Q. Is Objective-C on iOS the same as on OS X?
A. For the most part, yes. OS X includes thousands of additional application programming interfaces (APIs), however, and provides access to the underlying UNIX subsystem.
Q. Can an if-then-else statement be extended beyond evaluating and acting on a single expression?
A. Yes. The if-then-else
statement can be extended by adding another if
statement after the else:
if (<expression>) {
// do this, the expression is true.
} else if (<expression>) {
// the expression isn't true, do this instead.
} else {
// Neither of the expressions are true, do this anyway!
}
You can continue expanding the statement with as many else-if
statements as you need.
Q. Why are primitive data types used at all? Why aren’t there objects for everything?
A. Primitive data types take up much less memory than objects and are much easier to manipulate. Implementing a simple integer within an object would add a layer of complexity and inefficiency that just isn’t needed.
Q. Why can’t I release objects if I know I have finished using them? How can ARC do a better job?
A. ARC is built around the idea that Objective-C is very structured and predictable. By being able to analyze your code at compile time, ARC and LLVM can optimize memory management in a way that mere mortals would have a difficult time replicating.
1. Start Xcode and create a new project using the iPhone or iPad Single View Application template. Review the contents of the classes in the project folder. With the information you’ve read in this hour, you should now be able to read and navigate the structure of these files.
2. Return to the Apple iOS Dev Center (http://developer.apple.com/library/ios) and begin reviewing the Learning Objective-C: A Primer tutorial.