[go: up one dir, main page]
More Web Proxy on the site http://driver.im/
 

A Quick Objective-C 2.0 Tutorial In the interest of getting started quickly, here's a quick tour of new features in Objective-C 2.0 which will probably affect about 80% of the work you do. We'll look at properties, dot syntax, fast enumeration, and garbage collection.

Properties

Properties are a generic way of declaring the data a class provides. In a Movie class, the properties would be things like title, studio, and yearReleased. Here's what the header for the Movie class looks like in Objective-C 1.x:

@interface Movie : NSObject {

  NSString* title;
  NSString* studio;
  int yearReleased;
}

+ (id)movie
;


- (NSString*)title
;
- (void)setTitle:(NSString*)aValue;
- (NSString*)studio;
- (void)setStudio:(NSString*)aValue;
- (int)yearReleased;
- (void)setYearReleased:(int)aValue;
- (NSString*)summary;

@end


Here's how it looks using Objective-C 2.0 and the new Leopard NSInteger type:

@interface Movie : NSObject {

  NSString* title;
  NSString* studio;
  NSInteger yearReleased;
}

+ (id)movie
;


@property (copy) NSString* title;
@property (copy) NSString* studio;
@property (assign) NSInteger yearReleased;
@property (readonly) NSString* summary;

@end


Notice that not everything is a property. There's a class method for generating new objects called +movie. There's no way (or need) to declare this as a property. The format is:

@property (<parameters>) <type> <name>;

The most-commonly used parameters are copy/retain/assign. You choose one to specify how the setter will be generated for the property. Many Objective-C objects are best used with retain, but since these are strings, we'll use copy.

The assign keyword will generate a setter which assigns the value to the instance variable directly, rather than copying or retaining it. This is best for primitive types like NSInteger and CGFloat, or objects you don't directly own, such as delegates. Keep in mind retain and assign are basically interchangeable when garbage collection is enabled.

The readonly keyword means a setter will not be generated, so it should not be used in combination with any of copy/retain/assign. We're declaring summary as readonly because there's no instance variable for it. Instead, we generate the contents for it on the fly.

Let's take a look at the implementation of this class in Objective-C 1.x:

@implementation Movie

+ (id)movie
{
  return [[[Movie alloc] init] autorelease];
}

- (NSString*)title
{
  return title;
}

- (void)setTitle:(NSString*)aValue {
  [title autorelease];
  title = [aValue copy];
}

- (NSString*)studio {
  return studio;
}

- (void)setStudio:(NSString*)aValue {
  [studio autorelease];
  studio = [aValue copy];
}

- (int)yearReleased {
  return yearReleased;
}

- (void)setYearReleased:(int)aValue {
  yearReleased = aValue;
}

- (NSString*)summary {
  NSNumber* yearAsObject;
  yearAsObject = [NSNumber numberWithInt:[self yearReleased]];

  return [NSString stringWithFormat:@"%@ by %@, released in %@",
    [self title], [self studio], yearAsObject
]
;
}

@end


And here's the Objective-C 2.0 version (with garbage collection enabled):

@implementation Movie

@synthesize title;
@synthesize studio;
@synthesize yearReleased;

+ (id)movie
{
  return [[Movie alloc] init];
}

- (NSString*)summary

{
  NSNumber* yearAsObject;
  yearAsObject = [NSNumber numberWithInteger:self.yearReleased];

  return [NSString stringWithFormat:@"%@ by %@. Released in %@.",
    self.title, self.studio, yearAsObject
]
;
}

@end


The @synthesize directive generates accessor methods for us and garbage collection means +movie doesn't have to autorelease the object it returns. In addition, we use self.title and self.studio instead of [self title] and [self studio].

Let's see how this looks from the perspective of a client of the Movie class:
Movie* newMovie = [Movie movie];
newMovie.title = @"The Incredibles";
newMovie.studio = @"Pixar";
newMovie.yearReleased = 2004;

NSLog (@"Movie summary: %@", newMovie.summary);


This, of course, prints the following in the console:
Movie summary: The Incredibles, by Pixar. Released in 2004.

You can use either of the two accessor forms in Objective-C 2.0. You're not required to use dot syntax to access properties. You can also use the dot syntax on classes which don't explicitly define properties. For example:
NSString* newString = [textField stringValue];
NSString* newString = textField.stringValue;


The difference between @property and @synthesize might seem a bit unclear at first. Think of @property as declaring a property exists. The @synthesize directive actually implements code for the accessors, if necessary.

Note: By default, synthesized accessors are atomic in that the getter is guaranteed to return a valid value even with multiple active threads. There's no cost for this if garbage collection is enabled. You can disable this behavior with the keyword nonatomic.

Fast Enumeration

In Objective-C 1.x, your main options for loops were the standard for and while loops from C, and NSEnumerators. In Objective-C 2.0, you can use fast enumeration, which looks like this:
NSArray* allMovies = self.movies;

for ( Movie* oneMovie in allMovies ) {
  NSLog ( @"%@\n", oneMovie.summary );
}

    
It's a much simpler syntax, much faster than a for loop, and much safer since trying to modify the collection while it's being enumerated will cause an exception.

Garbage Collection

There's not much to say about garbage collection in a introductory-level tutorial. For the most part, it should "just work" in simple cases. Once you enable it, retain, release, autorelease and the other memory management methods have no effect. The copy and mutableCopy messages, of course, still copy the value of the object.

Garbage Collection is off by default. You enable it by double-clicking a target in your project and changing the "Objective-C Garbage Collection" setting to "Required":

Xcode Enable Garbage Collection


Garbage collection does not collect CF-style objects, such as CGImageRef, and you obviously still need to handle manually-allocated memory with malloc() and free(). There are a number of keywords you can use to get finer-grained control, such as specifying weak references. Those details are outside of the scope of this tutorial.

Keep in mind that the dealloc method is not called when garbage collected is on, but you can implement -finalize for some (though not all) of the same cases. Garbage collection eliminates retain cycles (objects which retain each other and are never released).

A Few Tips

When using properties with the dot syntax, prefix the name with self to use the accessor:
// direct access
value = studio;
studio = value;

// uses accessor methods, sends KVO notifications
self.studio = value;
value = self.studio


Even if you have garbage collection enabled, you should still use the accessor methods in most cases because Key-Value Observing, and Cocoa Bindings depend on these methods being called to synchronize changes across objects.

The NSInteger, NSUInteger and CGFloat types are architecture-safe versions of int, unsigned int and float/double. They'll shield you from the underlying processor-specific issues. If you're targeting Leopard, you should use these instead of the ANSI C alternatives. NSNumber has new methods for these types:
- (id)initWithInteger:(NSInteger)value;
- (id)initWithUnsignedInteger
:(NSUInteger)value;
- (NSInteger)integerValue;
- (NSUInteger)unsignedIntegerValue;
+ (NSNumber *)numberWithInteger:(NSInteger)value;
+ (NSNumber *)numberWithUnsignedInteger:(NSUInteger)value;


If you need to access a C struct in an Objective-C object, you may need to explicitly use brackets:
// "bounds" is a C struct. may be too ambiguous for the compiler
CGFloat value = self.bounds.origin.y;

// works reliably
CGFloat value = [self bounds].origin.y


Wrap-up

There's a lot more ground to cover, but this should give you a good start. We can look at more advanced topics in future tutorials. The 64-bit runtime has some features that the 32-bit version does not, such as the ability to synthesize instance variables. All of the hardware Apple currently ships is 64-bit, but we're a ways off from 64-bit being a majority of the installed base.

Remember that all of the new features in Objective-C 2.0 are optional. Aside from some lower-level runtime functions and hacks, anything that was valid code in Objective 1.x is valid in 2.0, as well.

For more information, see the Objective-C 2.0 Overview at Apple Developer Connection.

(thanks to Jesper for proofreading, as usual)
Design Element
A Quick Objective-C 2.0 Tutorial
Posted Oct 28, 2007 — 38 comments below




 
Magnus Nordlander — Oct 28, 07 4870
Great article!

Although, you may want to remove -setSummary: from the very first code snippet, seeing as it's probably shouldn't be there.
Kenneth Ballenegger — Oct 28, 07 4871
Great article, very informative! I don't understand why we shouldn't return an autoreleased instance for +movie though... Even though the garbage collector can pick up the stuff you don't explicitly release, wouldn't it be better (and more efficient) to do it yourself anyway? I mean, why not just return an autoreleased object?
Joachim Bengtsson — Oct 28, 07 4872
Indeed, nice introduction.

However, there's one thing I'm a bit worried about. Your +[movie] method doesn't autorelease its value, because it's redundant on GC'd code, I get that. However, I worry that if one writes code like that before the GC is very mature and common (say, three years from now?), one might get problems later on. Say you have to disable GC in your app because of some GC bug, or maybe you reuse the code in another project that doesn't have GC. From experience, adding correct memory usage /afterwards/ is a real PITA and you always miss cases you would've caught while writing the code. (This is of course not nearly as true in Cocoa as in C++, but still).

What's your view on this, Scott? And the other readers, of course. Should one keep writing retain-correct code for the time being even in GC'd apps?
Johan Kool — Oct 28, 07 4873
If a property is created for a NSMutableArray will the correct KVO notifications be sent when objects are inserted in, removed from or replaced into the array?
Mithras — Oct 28, 07 4874
One question: suppose I want to customize the setter, but not the getter.
Can I use @synthesize, then tack on my own setter?
Or is @synthesize an all-or-nothing deal, and hence to write my own setter, I must also write a simple little getter implementation?
Jonas Witt — Oct 28, 07 4875
stringWithFormat: supports more than just %@ placeholders, so you could save three lines of code and write the summary method as

- (NSString*)summary { return [NSString stringWithFormat:@"%@ by %@. Released in %d.", self.title, self.studio, self.yearReleased]; }
alastair — Oct 28, 07 4876
Just thought I’d point out that you can make CF objects collectable if you want to, by calling CFMakeCollectable().
Joachim Mrtensson — Oct 28, 07 4877

The 64-bit runtime has some features that the 32-bit version does not, such as the ability to synthesize instance variables.

I am trying to find documentation for what you mean by this, where is this documented?

Brian Christensen — Oct 28, 07 4878
@Mithras:

One question: suppose I want to customize the setter, but not the getter.
Can I use @synthesize, then tack on my own setter?


You can indeed customize the setter or getter and use @synthesize at the same time.
Jens Alfke — Oct 28, 07 4879
@Kenneth: No, there is no reason to call -retain, -release or -autorelease in a garbage-collected app. They are no-ops. In a garbage-collected environment, the runtime does not track the ref-counts of objects at all; instead, it scans the object graph to find which objects aren't being used anymore.

@Joachim: If you want the code to work correctly without garbage-collection enabled, then you have to write it just as you did pre-Leopard, paying attention to all of the retains/releases. And in that case you might as well just turn off GC and get a slight boost in speed and memory usage.

GC is wonderful. It's such a relief not to have to worry about all of that ref-counting gunk anymore...
Jesper — Oct 28, 07 4880
Joachim Mårtensson: The changes between the 32-bit and 64-bit Objective-C runtime are documented in http://developer.apple.com/leopard/devcenter/docs/releasenotes/Cocoa/RN-ObjectiveC/index.html (ADC free account login required).

Basically, 32-bit Objective-C classes store instance variables (ivars) in something close to a struct somewhere in its own class layout (which is also a struct - `id` is the name for a struct whose first field is an isa of type Class). You can use the deprecated @defs keyword to recreate the ivars inline in another struct if you's like (and you probably don't). This means that the size of the direct object varies depending on how many ivars you have.

With the 64-bit Objective-C runtime they're doing changes they've liked to do for all these years on the 32-bit runtime but can't do because it would break binary compatibility. One of those changes are "non-fragile ivars" which means that they're instead stored in another opaque container, and that means that the size of the object is instead constant, leading to better binary compatibility.

For example, if you make a framework with a class that has one ivar, and compile a 64-bit version of it, and an application uses that framework, and you then put out a new version with identical API but two new ivars inside that class, and you swap the old framework with the new framework, Everything Will Just Work.
Jesper — Oct 28, 07 4881
Uh - correction to my own comment.

`id` is the type of a pointer to a struct whose first field has the type Class.

typedef struct objc_object { Class isa; } *id;
and so on.
Henrik N — Oct 28, 07 4882
Thanks for the run-through.

I'm afraid Jesper missed a couple of typos ...
[Ed: clipped since typos are fixed -Scott]
...
;)
Jesper — Oct 28, 07 4883
Henrik: By "proofreading", Scott meant how he sent me a link and how I in return bitched about "nonatmic" being wrong and how he used "NSString* foo" instead of "NSString *foo".

But yes, thanks for letting me know how much I suck at proofreading. ;)
britt — Oct 28, 07 4884
Just spotted something... in this declaration, assign is redundant; as it's the default.

@property (assign) NSInteger yearReleased;

Also, I should point out that this will bite you (but not too hard, the compiler will issue warnings) if you leave out retain or copy on object-type properties in a non-gc environment.
Scott Stevenson — Oct 28, 07 4885 Scotty the Leopard
@Joachim Bengtsson: However, I worry that if one writes code like that before the GC is very mature and common (say, three years from now?), one might get problems later on

The collector is not experimental at this point. Xcode, for example, uses it. I've personally used it quite a bit.

Say you have to disable GC in your app because of some GC bug, or maybe you reuse the code in another project that doesn't have GC.

If you want to be absolutely sure, you can add all your retains and releases everywhere. But keep in mind this is only useful if you're constantly testing that code. That said, if you have a framework or library, I think it's better to not use GC so you don't limit your audience just to folks who have it enabled.

@Johan Kool: If a property is created for a NSMutableArray will the correct KVO notifications be sent when objects are inserted in, removed from or replaced into the array?

I forgot to mention this, but mutable foundation objects are not supported by the synthesized accessors. So you can declare the properly as NSMutableArray, but you have to implement the setter yourself so you can do -mutableCopy. The point is moot in this case, however, because NSMutableArrays do not normally publish changes when you alter them directly.

@Jonas Witt: stringWithFormat: supports more than just %@ placeholders, so you could save three lines of code and write the summary method as

True, but I was just demonstrating how to use NSNumber with the new methods. However, keep in mind that not all NSIntegers are 32-bit, so %d won't necessarily do what you expect.

@Henrik N: I'm afraid Jesper missed a couple of typos

I apologize for the typos. It was very late. :) They'll be fixed. (Fair warning, I'm may to edit your comment after I fix them since it will just be eating up space. In the future, it's probably better to just email me.)

@britt: in this declaration, assign is redundant; as it's the default

True, but I left it in intentionally for the purposes of this tutorial.
Steven Quinones-Colon — Oct 28, 07 4886
I'm really new to Objective C, and purposely resisted for the last couple of months to do much programming so I could wait for the new syntax stuff and learn everything at once, now I'm glad I did. One thing that has always bother me, and can't really find an explanations anywhere about what seems to me differences between C and Objective-C in pointer notation.
What's the difference between:
NSString* foo and NSString *foo

and why this?:
- (NSString*)title { return title;}

and not this:
- (NSString)*title { return title;}

and why this?:
Movie* newMovie = [Movie movie];

and not this:
Movie *newMovie = [Movie movie];

Again, a novice here coming from Python, so please be gentle.
Best regards.
Clark Cox — Oct 28, 07 4888
What's the difference between:
NSString* foo and NSString *foo


Nothing, as far as the compiler is concerned.

and why this?: - (NSString*)title { return title;}
and not this: - (NSString)*title { return title;}


The latter is a syntax error :)

and why this?: Movie* newMovie = [Movie movie];
and not this: Movie *newMovie = [Movie movie];


Style. Like the NSString example above, as far as the compiler is concerned, those are identical.
Jonas Witt — Oct 28, 07 4889
@Scott: Good point, I missed that. Let's just hope the years won't overflow 32 bits too soon. ;-)
mmalc — Oct 28, 07 4891
If you want to be absolutely sure, you can add all your retains and releases everywhere. But keep in mind this is only useful if you're constantly testing that code.

There really isn't any point in doing this, unless you're compiling for both GC and non-GC and that only makes sense if you're writing a framework. There are several design patterns and idioms that are different between the two environments (see the documentation). You should choose one and stick to it.

Re properties:

For differences between the 32- and 64-bit runtimes see Objective-C Language > The Objective-C 2.0 Programming Language > Properties > Runtime Differences.

By default, synthesized accessors are atomic in that the getter is guaranteed to return a valid value even with multiple active threads.

It is probably useful to add, per the documentation, that "atomic" does not mean "thread safe". Thread safety cannot be expressed at the individual accessor method level. The atomic attribute simply guarantees that the value returned from the getter or set via the setter is always fully retrieved or set.

For some additional examples of using Objective-C 2.0, see Cocoa Bindings Examples and Hints.

mmalc
Blain — Oct 29, 07 4892
Proofreading Obj-C, but leaving the english untouched. Why does it seem so appropriate?

The logic behind NSString *foo over NSString* foo is in how the C compiler reads things. (NSString *) is the data type either way, but if you had NSString* foo,bar;, you'd assume that both foo and bar are NSString*s. But the compiler reads it more like NSString *foo, bar;- foo is NSString*, what we want, but bar is only NSString struct, not the all important pointer, and things would break. You'd need NSString *foo, *bar;.

Yes, the compiler doesn't care about whitespace, it's purely for the reader's benefit, and all carries from when it was a case of int foo, *bar being the same as int *bar, foo.

BTW, is there any chance for an inline code tag? Something that simply uses a monospace font instead of bold or italic?
Scott Stevenson — Oct 29, 07 4894 Scotty the Leopard
@mmalc: There really isn't any point in doing this, unless you're compiling for both GC and non-GC and that only makes sense if you're writing a framework

I agree. But Joachim specifically said he wanted the option to switch off garbage collection, if necessary. I don't think many people will be switching off GC, though.
mmalc — Oct 29, 07 4895
I agree. But Joachim specifically said he wanted the option to switch off garbage collection, if necessary.

I'm not sure how this modifies what I wrote? In general, you should not do this. The only exception should be if you're writing a framework. As my paragraph went on, "There are several design patterns and idioms that are different between the two environments" (as described in the documentation). If you're writing an app, choose GC or not-GC and stick to whichever you choose. If you try to maintain both in an app, you're likely to be asking for a world of pain. And if you already have an app that doesn't use GC, you should generally not just delete the memory management code. If you want to move to GC, start over.

mmalc
Peter Hull — Oct 29, 07 4896
Does any of this work for earlier versions of OS X - i.e if I want to write a binary that works on 10.4 and 10.5, which Obj-C 2.0 features can I use?
Scott Stevenson — Oct 29, 07 4897 Scotty the Leopard
@Peter Hull: Does any of this work for earlier versions of OS X - i.e if I want to write a binary that works on 10.4 and 10.5, which Obj-C 2.0 features can I use?

All Objective-C 2.0 features require the new Leopard runtime, so Leopard only.
Bill Bumgarner — Oct 29, 07 4898
[A public] Thank you for putting this post together. Much appreciated.
Joachim Bengtsson — Nov 01, 07 4934
Scott, mmalc, et al: I think I dare to take the plunge now, thanks :) It /does/ seem a fair bit more sane to ignore all memory management when the GC is on...
Richard Albury — Nov 09, 07 5047
Good article, Scott: thanks.

Some typos still remain, though:
- You chose one should be You choose one
- obviously still need handle should be obviously still need to handle, and the malloc() and free() should perhaps be in code format

Thanks again.
Chris Welton — Dec 19, 07 5229
Honestly, I do not like the idea of objc "garbage collection" or [.] method notation.

I mean, seriously, imo, this is just "worse is better" mentality to the n'th degree. The main reason I love objc is that it WASN'T that...

This can only lead to more obscure bugs being written by programmers with even less of a clue as to what it is they are actually "doing". (Who will of course have no idea what they are doing wrong...)

You may not believe me yet, but wait a while.

Oh, and P.S., objc (pre 2.0) had the best memory management system I've ever seen... After I learned how to use it (Which very few people ever bother to learn, btw, esp. those who write tutorials on the matter.) I never wanted to go back to another system.
Gav Newalkar — Jan 24, 08 5391
Hi there,

I've started to learn objective-C / cocoa and i'm from a strong Java background.

One of my major gripes with objective-c is that you define instance variables within the interface. This goes against what i've learnt, which is that instance variables are implementation details.

What if i want to implement a class with my own set of instance variables? Can i define them in the interface section or am I forced to creat a sub-class?

Moreso, what is the motive behine Apple to so this way in making developers specify instance variables within an interface?

Gav
Scott Stevenson — Jan 24, 08 5394 Scotty the Leopard
@Gav Newalkar: One of my major gripes with objective-c is that you define instance variables within the interface. This goes against what i've learnt, which is that instance variables are implementation details.

I think it's possible the term "interface" might be causing confusion. In Objective-C, an @interface is just the class definition. I believe Java uses the term "interface" for what Objective-C would call a "protocol" definition.

In Objective-C, the @interface section is where you list the instance variables and public methods for that class. If you use the new 64-bit Objective-C runtime in Leopard, you can synthesize the instance variables, effectively hiding them from the public interface.
Matt Patenaude — Jun 02, 08 6006
I'd just like to point out one unrelated point about the +movie convenience method.

While that is the most common form of such a convenience method, I've found that this is generally safer and more powerful:

+ (id)movie { return [[[self alloc] init] autorelease]; }

The benefit of using self to do the initialization is that the +movie method does not have to be reimplemented should Movie be subclassed-- it will continue to return the correct object, ie, it will return a Movie instance when called on the Movie class, and a HorrorMovie instance when called on the HorrorMovie class, without modification.
Scott Stevenson — Jun 03, 08 6007 Scotty the Leopard
@Matt Patenaude: The benefit of using self to do the initialization is that the +movie method does not have to be reimplemented should Movie be subclassed

Agreed.
Frank V — Mar 27, 09 6675
Very well written and very helpful over all. Thank you much!
Mike A — Jun 01, 09 6792
I wrote a lot of code in Obj C pre 2.0 and remember always wishing for gc. That's good they put it in. There always seem to be odd cases of retain cycles in non trivial apps. So that's good.

But @synthesize? Arrgh! When will language designers learn?

We don't need getters and setters, we don't need ways to speed up the writing of them.

I wonder what Brad thought of that?
trenthm — Jul 14, 09 6832
Bravo! This tutorial was perfect for me just getting back into ObjC. Thanks, Scott!

hands.thumbDirection = @"Up";

haha!
CY — Jul 31, 09 6843
Hi There,

I am new to Obj - C, I don't understand
this line

yearAsObject = [NSNumber numberWithInteger:self.yearReleased]; return [NSString stringWithFormat:@"%@ by %@. Released in %@.", self.title, self.studio, yearAsObject];

can we just use self.yearReleased to return the int ?

return [NSString stringWithFormat:@"%@ by %@. Released in %@.", self.title, self.studio, self.yearReleased];


thanks in advance
j o a r — Aug 15, 09 6850
You can certainly do that. Would have to change it like this though:

return [NSString stringWithFormat:@"%@ by %@. Released in %ld.", self.title, self.studio, self.yearReleased];




 

Comments Temporarily Disabled

I had to temporarily disable comments due to spam. I'll re-enable them soon.





Copyright © Scott Stevenson 2004-2015