About
This is the M Cubed Software weblog. To find out more about us head to our about page.
Search
Feed
Archives
- September 2010
- June 2010
- April 2010
- March 2010
- February 2010
- January 2010
- November 2009
- August 2009
- July 2009
- May 2009
- April 2009
- March 2009
- February 2009
- January 2009
- December 2008
- November 2008
- October 2008
- September 2008
- August 2008
- July 2008
- June 2008
- May 2008
- April 2008
- March 2008
- January 2008
- December 2007
- November 2007
- October 2007
- September 2007
- August 2007
- July 2007
- June 2007
- May 2007
New Objective-C Features [UPDATEDx2]
Posted on 25/06/2010 at 08:54 PM in Coding
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:
- Your app must use the modern runtime (so no 32 bit Mac apps)
- You have to use LLVM 1.5 or higher as your compiler
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:
- -Xclang
- -fobjc-nonfragile-abi2
So now we're all set up, what the hell are these features?
@synthesize by Default
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
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:
==========MyClass.h==========
@interface MyClass
@end
==========MyClass.m==========
@interface MyClass () {
id internal1;
}
- (void)internalMethod;
@end
@interface MyClass () {
id internal2;
}
@end
@implementation MyClass
- (void)internalMethod {
NSLog(@"%@ %@", internal1, internal2);
}
@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.
(10) Comments
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