Commitment Issues

ARC is one of most amazing advances in the history of Objective-C; really in the history of compiler technologies in general.

It’s really cool.

But….

We can’t all just make the jump right this second. No matter how good the ARC migration tool is, there’s a lot of code floating around and the time to migrate and retest can be insurmountable. An intermediary solution would be writing code that works equally well in ARC and non ARC projects.

Enter macros

LLVM, the thing that makes ARC possible, offers some very useful ways to probe for the availablility of features, most notably __has_feature(). We can test for ARC using objc_arc and weak using objc_arc_weak

  • __has_feature(objc_arc)
  • __has_feature(objc_arc_weak)

These macros expand into 0 or 1 based on the features being turned on at compile time. From there we can build a couple more useful macros to make writing ARC code easier.

  • HASARC - Shorthand version of __has_feature(objc_arc)
  • HASWEAK - Shorthand version of __has_feature(objc_arc_weak)
  • STRONG - Expands to strong for ARC and retain for non ARC (useful for property declarations)

  • __STRONG - Expands to __strong for ARC and to nothing for non ARC (useful for variable declarations)
  • WEAK - Expands to weak for ARC, assign for non ARC and unsafe_unretained for ARC without weak support (useful for property declarations)
  • __WEAK - Expands to __weak for ARC, to nothing for non ARC environments and to __unsafe_unretained for ARC without weak support (useful for variable declarations)
  • IF_ARC(ARCBlock, NOARCBlock) - Function style macro for larger blocks of code that are different in ARC
  • NO_ARC(NoARCBlock) - Function style macro for code that only executes when ARC isn’t available
  • NO_WEAK(NoWeakBlock) - Function style macro for code that only executes when ARC isn’t available or doesn’t support weak (iOS 4, OS X 10.6)

Example

@interface Foo : NSObject
@property (WEAK, nonatomic) id delegate;
@property (STRONG, nonatomic) id observer;
@end

@implementation
@synthesize delegate=_delegate;
@synthesize observer=_observer;
- (void)dealloc
{
	[[NSNotificationCenter defaultCenter] removeObserver:_observer];
	NO_ARC(
		[_observer release];
		[super dealloc];
	)
}
- (id)init
{
	self = [super init];
	if (self)
	{
		// __block was a weak reference pre ARC, but is now a 
		__WEAK __block __typeof__(self) bSelf = self;
		self.observer = 
		[[NSNotificationCenter defaultCenter] addObserverForName:UIApplicationWillDoSomething object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
			NSLog(@"This is me: %@", bSelf);
		}];
	}
	return self;
}
@end

@interface Bar : NSObject
@property (STRONG, nonatomic) Foo *foo;
@end

@implementation Bar
@synthesize foo=_foo;
- (void)dealloc
{
	NO_WEAK(
		_foo.delegate = nil;
	)
	NO_ARC(
		[_foo release];
		[super dealloc];
	)
}
- (id)init
{
	self = [super init];
	if (self)
	{
		_foo = [Foo new];
		_foo.delegate = self;
	}
	return self;
}
@end

It’s available for download on my Github.

Use and enjoy and provide feedback.


FMDB covers very similar ground using a set of macros that wrap retain/release/autorelease.

Updated: