Wherein I document a case of my ignorance in the hopes you can avoid going down the same rat hole. This is a bit long and rambling, my apologies for that.
As you may know, gentle reader… I have decided to go full steam ahead to converting my apps to ARC.. It’s been a fun process… with it’s own share of complexities, but overall, not too bad. My first converted app, Wasabi, was fairly easy. However, there are a few design patterns I tend to use in my code which skirt the rules of prudence when writing manual retain and release Objective-C, so I was a bit concerned how ARC would handle them. To some, the solutions may be obvious, but they weren’t at all obvious to me, so I’m documenting them here, in case someone else should run into a similar issue. In this case, the issue turned out not to be anything to do with ARC at all, but my tracking it down was educational nonetheless, and useful, I suppose, to talk about.
So, without further ado, I offer you the following manual retain and release Objective-C code:
- (IBAction)buttonPushed:(id)sender
{
NSURLRequest *req = [NSURLRequest requestWithURL:
[NSURL URLWithString:@"http://ax.phobos.apple.com.
edgesuite.net/WebObjects/MZStoreServices.woa/
wa/wsSearch?term=zen"]];
JSONWebCommand *command = [[JSONWebCommand alloc]
initWithRequest:req];
command.completionBlock = ^(NSDictionary *result)
{
NSLog(@"Complete. Result: %@", result);
};
command.finallyBlock = ^
{
NSLog(@"Finished");
[command release];
};
NSLog(@"Starting load...");
[command start];
}
So, in this code… there’s this JSONWebCommand class, whose purpose is to wrap an NSURLConnection and make it simpler to use and give it some block capabilities. Basically, no need to implement delegate callbacks and things, you just give it blocks instead of a delegate, and it automagically parses the resultant response from JSON to an NSDictionary. Everything is nice and clean. The finallyBlock is always called, regardless of whether the command completes successfully or not. So I used this in the past, as shown here, to release my command ([command release]).
This way I could make this call be very fire and forget. That is, I fire off this command, and I forget about it. I don’t need to hold onto an instance of my command or anything. It’ll call me back when it’s done. It does what I want it to do, and then is released.
ARC converted this code to:
- (IBAction)buttonPushed:(id)sender
{
NSURLRequest *req = [NSURLRequest requestWithURL:
[NSURL URLWithString:@"http://ax.phobos.apple.com.
edgesuite.net/WebObjects/MZStoreServices.woa/
wa/wsSearch?term=zen"]];
JSONWebCommand *command = [[JSONWebCommand alloc]
initWithRequest:req];
command.completionBlock = ^(NSDictionary *result)
{
NSLog(@"Complete. Result: %@", result);
};
command.finallyBlock = ^
{
NSLog(@"Finished");
};
NSLog(@"Starting load...");
[command start];
}
Not much different… but a key issue here… my release is gone!
Of course, that’s exactly what ARC is supposed to do, but WTF! I thought… urm… I’m gonna start it and it’s going to be immediately released! That won’t work!
But it did work. It worked just fine. It appeared all of the cases of this code worked without crash and without issue. How could this be?
Based on my knowledge of what ARC was doing… my command should be deallocated at the end of this method and hence none of the blocks should get called. They were, however, and this was perplexing to me!
I posted a subset of this code on the developer forums, and Greg Parker informed me that if I wanted to get my desired behavior, that is, an object that contains a block which deallocates the object, a good way to do it would be to do:
__block Foo *foo = [[Foo alloc..]];
foo.completionBlock = ^{ foo = nil; };
[foo start];
Essentially specifying an __block modifier for the variable and then setting it to nil in the completion block. Sure enough, this sounded like exactly what I wanted, and is… in other cases than this, probably a perfectly valid pattern to use for this kind of code. Still, I was perplexed that since I had this code sprinkled through my project, why was I not encountering any issues?
Finally, I decided I wanted to track down exactly what the compiler was doing here to educate myself a bit more on how ARC works.
I looked for a tool in Xcode that could give me a listing of where all the retains and releases were being inserted, but I found no joy.
To be clear… since I wasn’t sure what was going on here… I needed to rule out two different issues… First, that the JSONWebCommand wasn’t being over released. If it was, it could ultimately crash my app. Second, that perhaps the JSONWebCommand wasn’t being released. In this case, I might wind up with a leak. The tool to do this in both cases is Instruments.
I knew that the latest version of Leaks is capable now of even detecting leaks that are a result of retain cycles. Retain cycles are among the few cases where ARC might leak memory. This is because one way to get ARC to not release your object is by having it be retained by something that owns it. (i.e.: A owns B which owns A). This is a retain cycle, and ARC does not detect nor prevent it. It’s really easy to make retain cycles using blocks because of the way that blocks capture local variables. For example:
self.foo = [[Foo alloc] ...];
foo.completionBlock = ^{ [self doBar]; };
In this case, foo is owned by self, and the call [self doBar] inside the block also captures self, strongly, within the block. The block is copied by foo, and so any objects captured within it are retained by foo. Since foo is also owned by self, this is a retain cycle. It’s insidious, and not obvious at all when you just look at it. It gets worse too, when you do this:
self.foo = [[Foo alloc] ...];
foo.completionBlock = ^{ [bar doSomething]; };
In this case, bar is a member variable on self. Because it’s a member variable, it still captures self, even though self is not explicitly shown. In other words, this is still a retain cycle.
In the past, we resolved these kinds of retain cycles using techniques that declared a new variable to hold self which is modified with the modifier __block. This is because, in the past, the __block modifier basically made a weak, writable reference on the heap to the variable in question. For example:
__block id blockSelf = self;
foo.completionBlock = ^{ [blockSelf doBar]; };
So this would break the retain cycle. Under ARC however, the behavior of __block has been changed! __block variables are now strong, so this will NOT solve your retain cycle! Instead, you should now use the following pattern, which takes advantage of the __weak modifier.
__weak id blockSelf = self;
foo.completionBlock = ^
{
id strongSelf = blockSelf;
if(strongSelf)
[strongSelf doBar];
};
This is the safest way to go, since blockSelf is a zeroing weak reference… when the block executes, if self has been deallocated, then your code won’t crash. Now, if you’re on iOS 4, or Mac OS 10.6, then you don’t have __weak… so instead, you can still get the same behavior that you had before by using __unsafe_unretained in conjunction with __block:
__block __unsafe_unretained id blockSelf = self;
foo.completionBlock = ^{ [blockSelf doBar]; };
Just realize that if self gets deallocated your app might blow up. Coder beware!
But anyway… I have digressed… none of this was what was happening in my code. Instruments showed no leaks… and in fact, I could see (by creating a dealloc method on my JSONWebCommand and putting a breakpoint in it) that my command was being deallocated. The only question was if it was being deallocated before I was done with it or not. I then ran the NSZombies instrument on the code… and it showed that it was not being prematurely deallocated. So that left me with the question of… How in the heck is ARC knowing the right thing to do here? So finally, I decided to do some hardcore code spelunking…
I asked around a bit on Twitter, and Stefen Lasser suggested that I use the Xcode debugger to set breakpoints on the ARC runtime methods themselves. You can do this yourself by setting symbolic breakpoints on objc_retain objc_release etc… This can be very noisy, however, so you’ll either want to disable them initially until you hit a specific point in your program, or tweak the conditions on the breakpoint to only break on certain classes. For example, you could set a conditional symbolic breakpoint on objc_retain such that it only fires when it’s parameter is of type JSONWebCommand (this may require some console gdb-fu).
In the end, this is exactly what I did… I basically ran my program up until my block of code… then set my breakpoints on the release/retain methods… and looked up the stack as each was called in turn… and here’s what I found:
This code…
connection = [[NSURLConnection alloc]
initWithRequest:request delegate:self startImmediately:NO];
is inside JSONWebCommand… can you spot it? Well it turns out… NSURLConnection retains it’s delegate! I’d gotten so used to delegates that are not retained, I had looked at this code a dozen times and thought “No, that can’t be retaining me… that’s a delegate!” But I traced it up to there, and saw the retain, and I said “What? How can that be retaining self?” I checked the documentation and… sure ’nuff… that’s the way that bugger works. It retains it when set, and releases it when the connection terminates.
So I guess in the end… what did I learn?
Well, I got some practice using the new tools in Instruments. Check them out… they’re excellent! In particular watch the WWDC videos on Instruments. Good stuff.
I also learned a bit about how to trace around inside ARC’s inserted code. Truth be known, I didn’t actually need the release in my block at all even before changing to ARC. So the tool was smarter than me! Less code is always better! I guess the key takeaway here, however, was that ARC wasn’t doing anything surprising. It was doing exactly what it should have done! I would have been far more bothered if it turned out it had some kind of magic it was working. But it wasn’t.