Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Adam Milligan

Why not to use ARC

Adam Milligan
Sunday, October 7, 2012

If you have done any development for iOS in the past few years you have at least some familiarity with ARC. The overall response to ARC since Apple released it with iOS 5 has been little short of orgasmic. You can’t swing a dead internet cat without hitting a blog post from someone explaining how ARC saved his/her dying grandmother and if you’re not using it on every project you touch then you’re helping the Commies win.

I’ve seen some projects do perfectly well with ARC, but at the same time I feel it provides its own set of challenges which we should not overlook. Here are some reasons why you might want to consider not using ARC on your next Objective C project.

ARC probably isn’t solving the problem you need it to

Memory management. Programmers whisper these words in fearful tones, or brazenly avow the impossibility of doing it correctly. ARC takes care of memory management, thus solving one of the great problems of our generation, right?

As it turns out, memory management is actually quite simple; /relationship/ management presents the challenge. Memory problems are usually a result of poor relationship management. Object A leaks because objects B and C both have an ownership relationship to A, A has an ownership relationship to C, etc.

In simple cases ARC will clean up the unused memory for you, but you’re still left with a poorly designed object graph, and all its associated problems. In more complex cases you can have strong circular references — a common result of a messy object graph — and then even ARC can’t prevent the leaks.

ARC makes easy things easier, but difficult things more difficult

Here’s a simple example of a class that has a simple relationship to another class:

Class declaration without ARC:

@interface Person : NSObject
@property (nonatomic, retain) Wallet *wallet;
@end

Class declaration with ARC:

@interface Person : NSObject
@property (strong, nonatomic) Wallet *wallet;
@end

Not much difference here. Here’s a bit of the implementation:

Initializer without ARC:

- (id)init {
    if (self = [super init];) {
        self.wallet = [[[Wallet alloc] init] autorelease];
    }
    return self;
}

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

Initializer with ARC:

- (id)init {
    if (self = [super init];) {
        self.wallet = [[Wallet alloc] init];
    }
    return self;
}

In this simple case ARC makes life a little easier; you don’t have to type autorelease, and you don’t have to write a dealloc method to release owned objects. With a well designed object graph, these two things are the vast majority of memory management you need to do. They’re more tedious than difficult, but ARC makes them go away. That’s pretty handy, right?

Now let’s look at an example of retrieving a value from the iOS Keychain, which is a bit more complicated:

Without ARC:

NSMutableDictionary *query = [NSMutableDictionary dictionary];
[query setObject:kSecClassGenericPassword forKey:kSecClass];
[query setObject:[NSData dataWithBytes:SOME_ID length:ID_LENGTH] forKey:kSecAttrGeneric];
[query setObject:(id)kCFBooleanTrue forKey:kSecReturnData];

NSData *result = nil;
NSString *value = nil
if (errSecSuccess == SecItemCopyMatching((CFDictionaryRef)query, (CFTypeRef *)&result)) {
    value = [[[NSString alloc] initWithData:[result autorelease] encoding:NSUTF8StringEncoding] autorelease];
}

With ARC:

NSMutableDictionary *query = [NSMutableDictionary dictionary];
[query setObject:(__bridge id)kSecClassGenericPassword forKey:(__bridge id)kSecClass];
[query setObject:[NSData dataWithBytes:SOME_ID length:ID_LENGTH] forKey:(__bridge id)kSecAttrGeneric];
[query setObject:(id)kCFBooleanTrue forKey:(__bridge id)kSecReturnData];

CFDataRef result = NULL;
NSString *value = nil;
if (errSecSuccess == SecItemCopyMatching((__bridge CFDictionaryRef)query, (CFTypeRef *)&result)) {
    value = [[NSString alloc] initWithData:(__bridge_transfer NSData *)result encoding:NSUTF8StringEncoding];
}

ARC saves us from two autorelease calls, but at the cost of five __bridge casts and a __bridge_transfer cast. ARC won’t let you declare the result variable as an NSData, since ARC disallows casting indirect pointers entirely. Thus, when you receive the result in a CFDataRef you’re still responsible for releasing that memory. You could use CFRelease() on it, or, as shown here, __bridge_transfer the CFDataRef into an NSData * temporary that ARC cleans up at the end of the execution of the statement.

Not exactly simpler, is it?

Sometimes ARC doesn’t help at all

Do you see the memory leak in this code?

self.thing = [Thing thing];
self.thing.completeNotification = ^{
    [self spreadTheWord];
};
[self.thing startTask];

By default, Objective C blocks retain everything they refer to from the local scope, including the self pointer. In this case, self retains the Thing, the Thing retains the block, and the block retains self. This creates a circular reference and a memory leak, with or without ARC.

Here’s how you fix it without ARC:

__block ThisClass *this = self;
self.thing.completeNotification = ^{
    [this spreadTheWord];
};

and with ARC:

__weak ThisClass *this = self;
self.thing.completeNotification = ^{
    [this spreadTheWord];
};

Even with ARC you still have to think about managing memory in some cases. If you rely on ARC to handle all of your memory this type of leak is probably more likely to happen.

The ARC compiler is broken

So ARC may not be helping as much as you thought, but at least it’s not really hurting anything, right? Unfortunately, turning on ARC injects bugs into the compiler. Until LLVM 4.1 (in XCode 4.5) code containing C++ templates wouldn’t even compile with ARC enabled. While LLVM 4.1 is a significant improvement over its predecessors, now the code compiles but fails to work correctly. Here’s an example of a simple function template with a specialization:

class Thing {
public:
    template<typename T>
    void do_something(const T &);
};

template<typename T>
void Thing::do_something(const T &) {
    NSLog(@"Do something with generic type");
}

template<>
void Thing::do_something(UIView * const &) {
    NSLog(@"Do something with UIView *");
}
</typename></typename>

When invoked:

Thing thing;
thing.do_something(someViewController.view);

here is the output without ARC:

2012-10-07 20:17:57.387 Project[65248:c07] Do something with UIView *

and with ARC:

2012-10-07 20:18:54.400 Project[65984:c07] Do something with generic type

What does C++ template type resolution have to do with Objective C reference counting? Nothing, as far as I can tell. It seems the ARC compiler is working off a different branch than the non-ARC compiler; a branch with significant unrelated defects. Who knows what other problems lie in wait?

Sometimes ARC is really, really broken

If you try digging around in the more esoteric capabilities of Objective C, ARC will sometimes get confused and do the wrong thing without warning you. For instance, if you’d like to change the class of an object at runtime, for example to a proxy class, with object_setClass, ARC won’t make a peep. However, it can get confused and release the object when its class changes, leading to overrelease and EXC_BAD_ACCESS. Worst of all, since ARC is completely out of your control, there’s nothing you can do about it!

Weirdly, for something that is meant to operate 100% at compile time, this behavior changes depending on what platform you run on. It happens in some instances on the simulator for older iOS versions, in different instances on the iOS 6 simulator, and (mercifully) never on a device.

ARC doesn’t really improve performance

This isn’t really a problem with ARC, but I’ve seen a number of blog posts about how great ARC is because it improves performance. These blog posts are usually filled with breathless tales of tail call optimization and assembly language listings. Now, there’s nothing wrong with Objective C from a performance perspective, but if you want to get down to questions of how many processor cycles your code takes then Objective C is pretty slow. Removing a few processor cycles from your iOS app is not going to have a noticeable effect.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Adam Milligan

iPhone on blocks: UITextFields

Adam Milligan
Saturday, August 7, 2010

If you’ve ever used a UITextField in an iPhone project (or, I suppose, an NSTextField in a Cocoa project) you know that you pass it a delegate object in order to respond to events. Handling the “Return” key press from the on-screen keyboard may look something like this (probably implemented in your view controller):

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    if (0 == [textField.text length]) {
        return NO;
    }
    [self doSomethingWithText:textField.text];
    [textField resignFirstResponder];
    return YES;
}

The delegate pattern is de rigueur for Cocoa classes, so you’ve likely never given this much special thought. Unless, that is, you decided at some point to have two text fields on screen at once. With two text fields you need to handle two sets of callbacks. You have a couple options for how to do this:

  1. Use the same delegate to handle both sets of callbacks, and use conditionals or switch statements to differentiate between the text fields.
  2. Create UITextField subclasses for each text field, each of which knows how to handle its own events. Each subclass will need a reference to the view controller, and you’ll need to expose methods in the view controller’s public interface for the subclasses to call, in order to effect some change in the system.
  3. Create a separate delegate class for each text field. As in the previous option, each delegate class will need a reference to the view controller and a way to send it messages to effect changes in the system.

None of these options feel particularly satisfactory: the second overuses inheritance, which the delegate pattern exists largely to avoid; both the second and third can result in class explosion; and the first feels so… procedural. Isn’t Object Oriented Programming supposed to save us from problems like this?

Procedural or no, Apple suggests the first option in all of their documentation and example code. An if statement isn’t really a big deal for two text fields; but what about three? Five? Ten? Your delegate method could look something like this:

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    if (textField == self.fooTextField) {
        if (0 == [textField.text length]) {
            return NO;
        }
        // Do something with the text
        }
    } else if (textField == self.barTextField) {
        // different logic...
    } else if (textField == self.batTextField) {
        // more different logic...
    } else if (textField == self.bazTextField) {
        // yet more different logic...
    } else if (textField == self.wibbleTextField) {
        // still yet more different logic...
    } else if ...
    [textField resignFirstResponder];
    return YES;
}

Or, alternately, you could resort to the dreaded switch statement. Either way, ugh.

The fundamental problem here is that only one object (let’s say a view controller) knows what to do when events occur, but only the subordinate objects (the text fields) know when the events occur. Each text field object encapsulates the behavior of its particular on-screen representation, but can’t access the internal state or implementation details of the view controller in order to effect changes to the system.

The recent addition of blocks to iOS can help us work around this problem. Since blocks are closures they capture and maintain their surrounding state at the point they’re instantiated. We can use this to define an implementation, and capture the internal state of our view controller, and pass all of this to an individual text field. Once done, each text field object will manage its own behavior without any intervention from the view controller whatsoever.

To make this work you’d need one (and only one) new class (theoretically, you could make a subclass of UITextField and set it to be its own delegate; unfortunately, setting a UITextField to be its own delegate seems to create an infinite loop deep in the bowels of Cocoa):

@interface BlockTextFieldDelegate : NSObject <UITextFieldDelegate>
@property (nonatomic, copy) BOOL (^textFieldShouldReturn)(UITextField *textField);
@end

@implementation BlockTextFieldDelegate
@synthesize textFieldShouldReturn = textFieldShouldReturn_;

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

- (BOOL)textFieldShouldReturn:(UITextField *)textField {
    if (self.textFieldShouldReturn) {
        return self.textFieldShouldReturn(textField);
    }
    return YES;
}

@end

Using the BlockTextFieldDelegate class might look like this (in your view controller):

- (void)viewDidLoad {
    [super viewDidLoad];
    self.textFieldDelegate.textFieldShouldReturn = ^ BOOL (UITextField *textField) {
        if (0 == [textField.text length]) {
            return NO;
        }
        [self doSomethingWithText:textField.text];
        [textField resignFirstResponder];
        return YES;
    };
}

The astute reader, armed with a passing familiarity with Apple’s Human Interface Guidelines, will point out that having several text fields on one screen may create a poor user experience. Certainly true for the iPhone, perhaps less true for the iPad’s larger screen. In any case, this technique works for any situation that requires multiple objects, not just UITextFields, reporting to a single delegate. Consider the case of multiple concurrent network requests, created by NSURLConnection objects, managed by a single view controller.

The astute reader will also point out that you may leave off the return type when defining blocks, assuming the compiler can infer it. I left the return type in to keep the example as explicit as possible, but the above block assignment could look like this (note the missing BOOL return declaration):

self.textFieldDelegate.textFieldShouldReturn = ^ (UITextField *textField) {

The block syntax isn’t beautiful, but you can use this technique to eliminate conditional chains, keep the number of classes and subclasses you create low, and avoid exposing the internal state of your objects; and that is beautiful.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (780)
  • rails (113)
  • testing (88)
  • ruby (83)
  • ruby on rails (70)
  • jobs (62)
  • javascript (55)
  • techtalk (44)
  • rspec (38)
  • ironblogger (32)
  • productivity (30)
  • activerecord (29)
  • gogaruco (29)
  • git (28)
  • nyc (27)
  • rubymine (26)
  • bloggerdome (23)
  • mobile (22)
  • process (21)
  • pivotal tracker (20)
  • cucumber (20)
  • jasmine (19)
  • design (18)
  • ios (18)
  • webos (17)
  • objective-c (17)
  • android (16)
  • palm (16)
  • "soft" ware (16)
  • fun (15)
  • tracker ecosystem (15)
  • ci (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • bdd (14)
  • gem (13)
  • css (13)
  • tdd (13)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
  • mojo (10)
  • chef (10)
  • api (10)
Subscribe to blocks Feed
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >