Learn Objective-C

虽然整篇都是英文,但是通俗易懂,不管是初学者还是深入者都是不错的梳理材料。

Cocoa Dev Central

Objective-C

Objective-C is the primary language used to write Mac software. If you'recomfortable with basic object-oriented concepts and the C language, Objective-Cwill make a lot of sense. If you don't know C, you should read the C Tutorialfirst.

This tutorial is written and illustrated by Scott Stevenson

Copyright © 2008 Scott Stevenson

1

Calling Methods

To get started as quickly as possible, let's look at somesimple examples. The basic syntax for calling a method on an object is this:

[object method];

[object methodWithInput:input];

Methods can return a value:

output = [object methodWithOutput];

output = [object methodWithInputAndOutput:input];

You can call methods on classes too, which is how youcreate objects. In the example below, we call the string method onthe NSString class, which returns a new NSString object:

id myObject = [NSString string];

The id type means that the myObject variable can refer to any kind of object, so the actualclass and the methods it implements aren't known when you compile the app.

In this example, it's obvious the object type will be an
NSString, so wecan change the type:

NSString* myString = [NSString string];

This is now an NSString variable, so the compiler will warn us if we try to usea method on this object which NSString doesn't support.

Notice that there's a asterisk to the right of the object type. All Objective-Cobject variables are pointers types. The
id type is predefined as a pointer type, so there's no needto add the asterisk.

Nested Messages

In many languages, nested method or function calls looklike this:

function1 ( function2() );

The result of function2 is passed as input to function1.In Objective-C, nested messages look like this:

[NSString stringWithFormat:[prefs format]];

Avoid nested nesting more than two message calls on asingle line, as it easily gets unreadable.

Multi-Input Methods

Some methods take multiple input values. In Objective-C,a method name can be split up into several segments. In the header, amulti-input method looks like this:

-(BOOL)writeToFile:(NSString*)path atomically:(BOOL)useAuxiliaryFile;

You call the method like this:

BOOL result = [myData writeToFile:@"/tmp/log.txt" atomically:NO];

These are not just named arguments. The method name isactually writeToFile:atomically: in theruntime system.

2

Accessors

All instance variables are private in Objective-C bydefault, so you should use accessors to get and set values in most cases. Thereare two syntaxes. This is the traditional 1.x syntax:

[photo setCaption:@"Day at the Beach"];

output = [photo caption];

The code on the second line is not reading the instancevariable directly. It's actually calling a method named caption. In mostcases, you don't add the "get" prefix to getters in Objective-C.

Whenever you see code inside square brackets, you are sending a message to anobject or a class.

Dot Syntax

The dot syntax for getters and setters is new inObjective-C 2.0, which is part of Mac OS X 10.5:

photo.caption = @"Day at the Beach";

output = photo.caption;

You can use either style, but choose only one for eachproject. The dot syntax should only be used setters and getters, not forgeneral purpose methods.

3

Creating Objects

There are two main ways to create an object. The first isthe one you saw before:

NSString* myString = [NSString string];

This is the more convenient automatic style. In thiscase, you are creating an autoreleased object, which we'll look at in more detail later. Inmany cases, though, you need to create an object using the manual style:

NSString* myString = [[NSString alloc] init];

This is a nested method call. The first is the alloc methodcalled on NSString itself. This is a relatively low-level call which reservesmemory and instantiates an object.

The second piece is a call to
init on the new object. The init implementation usually doesbasic setup, such as creating instance variables. The details of that areunknown to you as a client of the class.

In some cases, you may use a different version of
init which takesinput:

NSNumber* value = [[NSNumber alloc] initWithFloat:1.0];

4

Basic Memory Management

If you're writing an application for Mac OS X, you havethe option to enable garbage collection. In general, this means that you don'thave to think about memory management until you get to more complex cases.

However, you may not always be working with an environment that supportsgarbage collection. In that case, you need to know a few basic concepts.

If you create an object using the manual
alloc style, you need to release the object later. You should not manually release anautoreleased object because your application will crash if you do.

Here are two examples:

// string1 will be released automatically

NSString* string1 = [NSString string];

// must release this when done

NSString* string2 = [[NSString alloc] init];

[string2 release];

For this tutorial, you can assume that an automaticobject will go away at the end of the current function.

There's more to learn about memory management, but it will make more senseafter we look at a few more concepts.

5

Designing a Class Interface

The Objective-C syntax for creating a class is verysimple. It typically comes in two parts.

The class interface is usually stored in the
ClassName.h file, anddefines instance variables and public methods.

The implementation is in the
ClassName.m file and contains the actual code for these methods. Italso often defines private methods that aren't available to clients of theclass.

Here's what an interface file looks like. The class is called Photo, so thefile is named
Photo.h:

#import <Cocoa/Cocoa.h>

@interface Photo :NSObject {

NSString*caption;

NSString*photographer;

}

@end

First, we import Cocoa.h, to pull in all of the basic classes for a Cocoa app.The #import directive automatically guards against including a single file multipletimes.

The
@interface says that this is a declaration of the class Photo. Thecolon specifies the superclass, which is NSObject.

Inside the curly brackets, there are two instance variables:
caption and photographer. Both are NSStrings, but they could be any object type, including id.

Finally, the
@end symbol ends the class declaration.

Add Methods

Let's add some getters for the instance variables:

#import <Cocoa/Cocoa.h>

@interface Photo : NSObject {

NSString* caption;

NSString* photographer;

}

- caption;

- photographer;

@end

Remember, Objective-C methods typically leave out the"get" prefix. A single dash before a method name means it's ainstance method. A plus before a method name means it's a class method.

By default, the compiler assumes a method returns an id object, and that allinput values are id. The above code is technically correct, but it's unusual.Let's add specific types for the return values:

#import <Cocoa/Cocoa.h>

@interface Photo : NSObject {

NSString* caption;

NSString* photographer;

}

- (NSString*) caption;

- (NSString*) photographer;

@end

Now let's add setters:

#import <Cocoa/Cocoa.h>

@interface Photo : NSObject {

NSString* caption;

NSString* photographer;

}

- (NSString*) caption;

- (NSString*) photographer;

- (void) setCaption: (NSString*)input;

- (void) setPhotographer: (NSString*)input;

@end

Setters don't need to return a value, so we just specifythem as void.

6

Class Implementation

Let's create an implementation, starting with thegetters:

#import "Photo.h"

@implementation Photo

- (NSString*) caption {

returncaption;

}

- (NSString*) photographer {

returnphotographer;

}

@end

This part of the code starts with @implementation and the class name, and has @end, just like the interface. All methods must appearbetween these two statements.

The getters should look very familiar if you've ever written code, so let'smove on to the setters, which need a bit more explanation:

- (void) setCaption: (NSString*)input

{

[caption autorelease];

caption = [input retain];

}

- (void) setPhotographer: (NSString*)input

{

[photographer autorelease];

photographer = [input retain];

}

Each setter deals with two variables. The first is areference to the existing object, and the second is the new input object. In agarbage collected environment, we could just set the new value directly:

- (void) setCaption: (NSString*)input { caption = input;}

But if you can't use garbage collection, you need to release the oldobject, and retain the new one.

There are actually two ways to free a reference to an object:
release and autorelease. The standard release will remove the reference immediately. Theautorelease method will release it sometime soon, but it will definitely stayaround until the end of the current function (unless you add custom code tospecifically change this).

The autorelease method is safer inside a setter because the variables for thenew and old values could point to the same object. You wouldn't want toimmediately release an object which you're about to retain.

This may seem confusing right now, but it will make more sense as you progress.You don't need to understand it all yet.

Init

We can create an init method to set inital values for ourinstance variables:

- (id) init

{

if ( self = [super init] )

{

[selfsetCaption:@"Default Caption"];

[selfsetPhotographer:@"Default Photographer"];

}

return self;

}

This is fairly self-explanatory, though the second linemay look a bit unusual. This is a single equals sign, which assigns the resultof [super init] to self.

This essentially just asks the superclass to do its own initialization. The
if statement isverifying that the initialization was successful before trying to set defaultvalues.

Dealloc

The dealloc method is called on an object when it is being removedfrom memory. This is usually the best time to release references to all of yourchild instance variables:

- (void) dealloc

{

[caption release];

[photographer release];

[super dealloc];

}

On the first two lines, we just send release to each ofthe instance variables. We don't need to use autorelease here, and the standardrelease is a bit faster.

The last line is very important. We have to send the message
[super dealloc] to ask the superclass to do its cleanup. If we don't dothis, the object will not be removed, which is a memory leak.

The dealloc method is not called on objects if garbage collection is enabled.Instead, you implement the
finalize method.

7

More on Memory Management

Objective-C's memory management system is calledreference counting. All you have to do is keep track of your references, andthe runtime does the actual freeing of memory.

In simplest terms, you
alloc an object, maybe retain it at some point, then send one release for eachalloc/retain you sent. So if you used alloc once and then retain once, you needto release twice.

That's the theory of reference counting. But in practice,there are usually only two reasons to create an object:

1. To keep it as an instance variable
2. To use temporarily for single use inside a function

In most cases, the setter for an instance variable should just
autorelease the old object, and retain the new one. You then just make sure to release it in dealloc as well.

So the only real work is managing local references inside a function. Andthere's only one rule: if you create an object with
alloc or copy, send it a release or autorelease message at the end of the function. If you create an object any otherway, do nothing.

Here's the first case, managing an instance variable:

- (void) setTotalAmount: (NSNumber*)input

{

[totalAmount autorelease];

totalAmount = [input retain];

}

- (void) dealloc

{

[totalAmount release];

[super dealloc];

}

Here's the other case, local references. We only need torelease the object created with alloc:

NSNumber* value1 = [[NSNumber alloc]initWithFloat:8.75];

NSNumber* value2 = [NSNumber numberWithFloat:14.78];

// only release value1, not value2

[value1 release];

And here's a combo: using a local reference to set anobject as an instance variable:

NSNumber* value1 = [[NSNumber alloc]initWithFloat:8.75];

[self setTotal:value1];

NSNumber* value2 = [NSNumber numberWithFloat:14.78];

[self setTotal:value2];

[value1 release];

Notice how the rules for managing local references areexactly the same, regardless of whether you're setting them as instance variablesor not. You don't need to think about how the setters are implemented.

If you understand this, you understand 90% of what you will ever need to knowabout Objective-C memory management.

8

Logging

Logging messages to the console in Objective-C is verysimple. In fact, the NSLog() function is nearly identical to theC printf() function, except there's an additional %@ token forobjects.

NSLog ( @"The current date and time is: %@", [NSDate date] );

You can log an object to the console. The NSLog functioncalls the description method on the object, and prints the NSString which isreturned. You can override the description method in your class to return acustom string.

9

Properties

When we wrote the accessor methods for caption and author earlier,you might have noticed that the code is straightforward, and could probably begeneralized.

Properties are a feature in Objective-C that allow us to automatically generateaccessors, and also have some other side benefits. Let's convert the Photo classto use properties.

Here's what it looked like before:

#import <Cocoa/Cocoa.h>

@interface Photo : NSObject {

NSString* caption;

NSString* photographer;

}

- (NSString*) caption;

- (NSString*) photographer;

- (void) setCaption: (NSString*)input;

- (void) setPhotographer: (NSString*)input;

@end

Here's what it looks like once converted to properties:

#import <Cocoa/Cocoa.h>

@interface Photo : NSObject {

NSString* caption;

NSString* photographer;

}

@property (retain)NSString* caption;

@property (retain)NSString* photographer;

@end

The @property is an Objective-C directive which declares the property.The "retain" in the parenthesis specifies that the setter should retain the inputvalue, and the rest of the line simply specifies the type and the name of theproperty.

Now let's take a look at the implementation of the class:

#import "Photo.h"

@implementation Photo

@synthesize caption;

@synthesize photographer;

- (void) dealloc

{

[caption release];

[photographer release];

[super dealloc];

}

@end

The @synthesize directive automatically generates the setters andgetters for us, so all we have to implement for this class is the deallocmethod.

Accessors will only be generated if they don't already exist, so feel free tospecify @synthesize for a property, then implement your custom getter or setterif you want. The compiler will fill in whichever method is missing.

There are many other options for the property declarations, but those areoutside of the scope of this tutorial.

10

Calling Methods on Nil

In Objective-C, the nil object is the functional equivalent to the NULL pointer in many other languages. The difference is that you can callmethods on nil without crashing or throwing an exception.

This technique used by the frameworks in a number of different ways, but themain thing it means to you right now that you usually don't need to check fornil before calling a method on an object. If you call a method on nil thatreturns an object, you will get nil as a return value.

We can also use this to improve our dealloc method slightly:

- (void) dealloc

{

self.caption = nil;

self.photographer = nil;

[super dealloc];

}

This works because when we set nil as an instancevariable, the setter just retains nil (which does nothing) and releases the oldvalue. This approach is often better for dealloc because there's no chance ofthe variable pointing at random data where an object used to be.

Note that we're using the
self.<var> syntax here, which means we're using the setter andpicking up the memory management for free. If we just directly set the valuelike this, there would be a memory leak:

// incorrect. causes a memory leak.

// use self.caption to go through setter

caption = nil;

11

Categories

Categories are one of the most useful features ofObjective-C. Essentially, a category allows you to add methods to an existingclass without subclassing it or needing to know any of the details of how it'simplemented.

This is particularly useful because you can add methods to built-in objects. Ifyou want to add a method to all instances of NSString in your application, youjust add a category. There's no need to get everything to use a customsubclass.

For example, if I wanted to add a method to NSString to determine if thecontents is a URL, it would look like this:

#import <Cocoa/Cocoa.h>

@interface NSString (Utilities)

- (BOOL) isURL;

@end

This is very similar to a class declaration. Thedifferences are that there is no super class listed, and there's a name for thecategory in parenthesis. The name can be whatever you want, though it shouldcommunicate what the methods inside do.

Here's the implementation. Keep in mind this is not a good implementation ofURL detection. We're just trying to get the concept of categories across:

#import "NSString-Utilities.h"

@implementation NSString (Utilities)

- (BOOL) isURL

{

if ( [selfhasPrefix:@"http://"] )

return YES;

else

return NO;

}

@end

Now you can use this method on any NSString. Thefollowing code will print "string1 is a URL" in the console:

NSString* string1 = @"http://pixar.com/";

NSString* string2 = @"Pixar";

if ( [string1 isURL] )

NSLog (@"string1 is aURL");

if ( [string2 isURL] )

NSLog (@"string2 is aURL");

Unlike subclasses, categories can't add instancevariables. You can, however, use categories to override existing methods inclasses, but you should do so very carefully.

Remember, when you make changes to a class using a category, it affects allinstances of that class throughout the application.

Wrap Up

This is a basic overview of Objective-C. As you've seen, the language ispretty easy to pick up. There's not much special syntax to learn, and the sameconventions are used over and over again throughout Cocoa.

If you'd like these examples in action, download the project below and lookthrough the source code:

LearnObjectiveC Xcode 3.0 Project (56k)

Thank you.

Cocoa DevCentral is a service mark of Tree House Ideas
Site design © 2004-2008 Scott Stevenson | Made with TextMate

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值