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":
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)
A Quick Objective-C 2.0 Tutorial
Posted Oct 28, 2007 — 38 comments below
Posted Oct 28, 2007 — 38 comments below
Magnus Nordlander — Oct 28, 07 4870
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
Joachim Bengtsson — Oct 28, 07 4872
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
Mithras — Oct 28, 07 4874
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
- (NSString*)summary { return [NSString stringWithFormat:@"%@ by %@. Released in %d.", self.title, self.studio, self.yearReleased]; }
alastair — Oct 28, 07 4876
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
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
@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
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
`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
I'm afraid Jesper missed a couple of typos ...
[Ed: clipped since typos are fixed -Scott]
...
;)
Jesper — Oct 28, 07 4883
But yes, thanks for letting me know how much I suck at proofreading. ;)
britt — Oct 28, 07 4884
@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
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
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
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
mmalc — Oct 28, 07 4891
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
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
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'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
Scott Stevenson — Oct 29, 07 4897
All Objective-C 2.0 features require the new Leopard runtime, so Leopard only.
Bill Bumgarner — Oct 29, 07 4898
Joachim Bengtsson — Nov 01, 07 4934
Richard Albury — Nov 09, 07 5047
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
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
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
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
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
Agreed.
Frank V — Mar 27, 09 6675
Mike A — Jun 01, 09 6792
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
hands.thumbDirection = @"Up";
haha!
CY — Jul 31, 09 6843
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
return [NSString stringWithFormat:@"%@ by %@. Released in %ld.", self.title, self.studio, self.yearReleased];