New Objective-C Features [UPDATEDx3]

Posted on the 25/06/2010 at 08:54 PM

Prior to Apple acquiring NeXT, Objective-C had had relatively little added to it since it became a fully fledge language rather than simply a C preprocessor back in the 80s. About the only thing added for many years after Apple acquired NeXT was the @try…@catch…@finally exception syntax.

Then in 2007 Apple released Leopard and with it Objective-C 2.0 and the modern runtime. This added features such as properties, fast enumeration, class extensions and garbage collection and improved areas such as protocols, while making the runtime far more flexible and less fragile to change. Last year with Snow Leopard, Apple added blocks and associative storage to the language in what can sort of be thought of as Objective-C 2.1.

At WWDC Apple announced a few more features, which I personally think of as Objective-C 2.2. These are "@synthesize by default" and "declare ivars in class extensions". These are features that I have wanted for quite a while and I am sure a lot of other Objective-C developers have wanted. So what are they and how do you get them?


Getting the Features

To get them there are two requirements:

If you fulfil those requirements then you're almost able to use them. All that is left to do is add these two flags to your "Other C Flags" build setting:

So now we're all set up, what the hell are these features?


@synthesize by Default

NB: This feature is no longer available and so should not be used. It may re-appear at some point but in the mean time we sadly have to write our @synthesize statements. I've left the section in for reference if/when it comes back

With the addition of properties, Apple was able to remove a lot of boilerplate code from our classes. If you are using the legacy runtime then you are able to write code like this:

==========MyClass.h==========

@interface MyClass {
    NSString *foo;
}

@property (copy) NSString *foo;

@end



==========MyClass.m==========

@implementation MyClass

@synthesize foo;

@end

It is a lot cleaner than what you had to write pre Obj-C 2.0, but it still has a lot of stuff to write. If you are using the modern runtime (ie are writing an iOS or 64 bit only Mac app) then you can get rid of the instance variable:

==========MyClass.h==========

@interface MyClass {}

@property (copy) NSString *foo;

@end



==========MyClass.m==========

@implementation MyClass

@synthesize foo;

@end

That's a bit cleaner, we're only having to sort out our property in two places rather than three, but it's still one too many. 95% of the time, you just want to synthesise the methods and instance variable and so the vast majority of property code in your implementation is the same. Well as you can probably guess, "@synthesize by default" means that this is done for you by the compiler by default. If you want to override it with an @dynamic statement or link it to a different instance variable you can, but for the rest of the time you can reduce your code to this:

==========MyClass.h==========

@interface MyClass {}

@property (copy) NSString *foo;

@end



==========MyClass.m==========

@implementation MyClass

@end

There is one slight gotcha with this. Due to what seems to be a compiler bug, you will get an error if you try to directly access the synthesised instance variable. Unfortunately the only ways to fix this are to either add @synthesize, add the instance variable or only access it via the property methods.

[Update] It seems that this isn't a bug but is instead expected behaviour. The @synthesize methods are generated at the end of the class if you don't specify them explicitly, as the compiler can't tell if the properties need to be default synthesised until it has got to the end of the implementation.

[Update 2] And now it seems the LLVM/Clang team are going to try and work around this and get it working as you would expect. Three cheers for the LLVM/Clang team!


Declare Instance Variables in Class Extensions & Implementations

So, we have got rid of the instance variables for our properties, but these are public anyway, it's not that big a deal if developers can see them in the header. However, what is left are the private instance variables, the ones only used internally in a class. We can hide methods we don't want as our public interface in our implementation file, but not our instance variables. Instead we end up with this:

==========MyClass.h==========

@interface MyClass {
    id internal1;
    id internal2;
}

@end



==========MyClass.m==========

@interface MyClass ()

- (void)internalMethod;

@end


@implementation MyClass

- (void)internalMethod {
    NSLog(@"%@ %@", internal1, internal2);
}

@end

Thanks to Objective-C 2.0 and class extensions we can put our internal method declaration into our .m file and also have the compiler check that it is implemented and warn us if it isn't (unlike if we added a simple category). What we haven't been able to do is do the same for our instance variables. Well we can now, so our code can now look like this:

==========MyClass.h==========

@interface MyClass
@end


==========MyClass.m==========

@interface MyClass () {
 id internal1;
 id internal2;
}

- (void)internalMethod;

@end


@implementation MyClass

- (void)internalMethod {
 NSLog(@"%@ %@", internal1, internal2);
}

@end

Our internal instance variables really are internal now, which makes the header effectively become purely our public interface. And if you are feeling really crazy, you can have multiple class extensions and maybe just put them in the @implementation:

==========MyClass.h==========

@interface MyClass
@end


==========MyClass.m==========

@interface MyClass () {
 id internal1;
}

- (void)internalMethod;

@end


@interface MyClass () {
 id internal2;
}
@end


@implementation MyClass {
    id internal3;
}

- (void)internalMethod {
 NSLog(@"%@ %@", internal1, internal2, internal3);
}

@end



These may only seem like small enhancements, but they help you reduce the code you need to write and make that which you do much neater. It is little enhancements like this that keep trickling out that makes the life of an Objective-C developer that much easier.





Comments

Isn’t the reason you can’t directly access an auto-synthesised property’s instance variable because there isn’t one? Plain C declarations (like ivars) aren’t dynamic like properties, so the runtime can’t figure it out later. To have a traditional ivar, you need to declare one.

Posted by Daniel Kennett on 25/06/2010 at  11:44 PM




The @synthesize ivar access bug should no longer be present. At least what they said is that you can access it directly using self->propertyName.

Posted by max on 25/06/2010 at  11:52 PM




For paid-up Apple developers, all of the above is covered in detail in WWDC 2010 Session #144 - “Advanced Objective-C and Garbage Collection Techniques” - I am very, very excited about using Objective-C 2.2!

Posted by Tony Arnold on 26/06/2010 at  09:07 AM




Too bad they didn’t add the automatic-release of retain/copy @properties. Or did they?

What piss me up so far is not specifically to add @synthesize for each @property I declare, but to have to implement “-(void)dealloc” simply to release those ivars associated with the @properties(copy/retain).

Actually what about memory mgmt for those cases? If we don’t declare an ivar associated with the @property, as in your second listing, how could we release it (for copy or retain @properties)? By setting self.myprop = nil?

Except if this is done automatically for the case the ivar is autogenerated, and THAT would be great!

Thanks

Posted by AliGator on 29/06/2010 at  02:19 PM




For @synthesise by default, what happens in dealloc if you cant use the the ivar? SHould you just set the property to nil?
i.e.

what used to be

- (void)dealloc {
  [popoverController release];
  [super dealloc];
}

will become


- (void)dealloc {
  self.popoverController = nil;
  [super dealloc];
}

Is this correct?

I was also wondering about readonly properties

Posted by Dave on 29/06/2010 at  09:34 PM




@Ali & Dave: It’s not ideal, but yes that would be how it is done.

@Ali: That might be useful, but it would be a bit of a wasted effort given that GC is on the Mac and (hopefully) soon on the iPhone. Far more useful would being able to mark properties as archivable and have NSCoding support provided

@Dave: For readonly properties the only real options are:

1. redefine it as readwrite in a class extension
2. add the ivar
3. write the @synthesize

Posted by M Cubed on 29/06/2010 at  11:05 PM




I thought that setting the property to nil was deprecated or at least not recommanded (for a reason I don’t remember), and that there was a real reason to prefer [myivar release], so is this kind of a regression of the Apple coding rules? I’m really intrigued by this memory management policy for those cases

Actually I am the kind of programmer who does not like the GC, and I am mostly programming on the iPhone those days, so w/o the GC.
And I don’t want to be forced to use the GC simply to have the feature of automatically call the “release” on the ivar associated with my @property(retain)...!

Posted by AliGator on 29/06/2010 at  11:11 PM




Thanks for the response (and the article)

Posted by Dave on 29/06/2010 at  11:19 PM




That is great news - I’m particularly happy about ivars in class extensions!

Posted by Stuart Carnie on 30/06/2010 at  09:20 PM




Actually, it’s one flag: “-Xclang -fobjc-nonfragile-abi2”
The “-fobjc-nonfragile-abi2” is an argument for “-Xclang” flag.

References:
http://digdog.tumblr.com/post/833744044/xclang-fobjc-nonfragile-abi2-is-single-flag-with

Posted by digdog on 21/07/2010 at  03:34 AM




Post a Comment

Commenting is not available in this weblog entry.

<< Back to main



Copyright © 2006-2012 M Cubed Software