Fun With the Objective-C Runtime: Run Code at Deallocation of Any Object

Update: This post is getting some attention lately, so I’ve updated it a bit to be less incorrect.

Sometimes when you’re debugging an application, especially one that you’ve inherited, you find yourself wondering when an object is released. Autorelease pools only compound the problem, delaying the actual release until the run loop is idle. In this post, I’ll show you how to take advantage of new features in the Objective-C runtime to run arbitrary code when any object—whether it’s your own or a part of Apple’s frameworks—is deallocated.

We’ll be taking advantage of the Objective-C runtime’s new associated objects behavior. When you associate an object with another object using retain or copy semantics, the runtime automatically handles releasing it at the appropriate time. So, if we want one object to be released when another object is deallocated, we simply associate them:

id objectToBeDeallocated;
id objectWeWantToBeReleasedWhenThatHappens;

objc_setAssociatedObject(objectToBeDeallocted,
                         someUniqueKey,
                         objectWeWantToBeReleasedWhenThatHappens,
                         OBJC_ASSOCIATION_RETAIN);

Now, when objectToBeDeallocated is deallocated, objectWeWantToBeReleasedWhenThatHappens will be sent a -release message automatically. The association policy passed as the last parameter to the function can be one of the following:

OBJC_ASSOCIATION_ASSIGN No memory management; the value is simply assigned.
OBJC_ASSOCIATION_RETAIN_NONATOMIC Retains the object non-atomically.
OBJC_ASSOCIATION_COPY_NONATOMIC Copies the object non-atomically.
OBJC_ASSOCIATION_RETAIN Retains the object atomically.
OBJC_ASSOCIATION_COPY Copies the object atomically.

Obviously, using OBJC_ASSOCIATION_ASSIGN won’t work for us, since it won’t cause the object to be retained. We also don’t want to use either of the copy policies, since we only want one copy of our objects around. For this example I’ll be using OBJC_ASSOCIATION_RETAIN, but not over OBJC_ASSOCIATION_RETAIN_NONATOMIC for any compelling reason.

Now that we know how to release an object when another is deallocated, we need to create an object to run arbitrary code at deallocation time. Blocks are an excellent tool for this, so I created a dead-simple class, JKBlockExecutor, to handle the running of the block:

typedef void (^voidBlock)(void);

@interface JKBlockExecutor : NSObject {
	voidBlock	block;
}

@property (nonatomic, readwrite, copy) voidBlock	block;

- (id)initWithBlock:(voidBlock)block;

@end

@implementation JKBlockExecutor

@synthesize block;

- (id)initWithBlock:(voidBlock)aBlock
{
	self = [super init];

	if (self) {
		block = Block_copy(aBlock);
	}

	return self;
}

- (void)dealloc
{
	if (block != nil) {
		block();
		Block_release(block);
	}

	[super dealloc];
}

@end

Now that we can pass arbitrary code to a JKBlockExecutor (and if you have a better name I’m all ears), we can make a category on NSObject to make the association for us:

const void *runAtDeallocBlockKey = &runAtDeallocBlockKey;

@interface NSObject (JK_RunAtDealloc)

- (void)runAtDealloc:(voidBlock)block;

@end

@implementation NSObject (JK_RunAtDealloc)

- (void)runAtDealloc:(voidBlock)block
{
	if (block) {
		JKBlockExecutor *executor = [[JKBlockExecutor alloc] initWithBlock:block];

		objc_setAssociatedObject(self,
								 runAtDeallocBlockKey,
								 executor,
								 OBJC_ASSOCIATION_RETAIN);

		[executor release];
	}
}

@end

So, how do you use it? The following example prints “Deallocating foo!” when foo is deallocated:

NSObject *foo = [[NSObject alloc] init];

[foo runAtDealloc:^{
	NSLog(@"Deallocating foo!");
}];

[foo release];

And that’s all there is to it!

Well, almost. There is one gotcha that I must warn you about: don’t access the object from within the block. There are two reasons. First, I’m not sure where in the deallocation process the Objective-C runtime releases its associated objects, so accessing the object may result in a crash. Second, if you reference the object from within the block, the block will retain the object. This causes a retain cycle where the block and the object each own each other, so neither will ever be released. If you absolutely must reference your object (at your own risk), then do it like so:

NSObject *foo = [[NSObject alloc] init];

__block id objectRef = foo;

[foo runAtDealloc:^{
	NSLog(@"Deallocating foo at address %p!", objectRef);
}];

[foo release];

Using the __block storage qualifier on an Objective-C object causes the runtime to avoid retaining the object, since the dymanics of object retain counts inside of blocks would be far too hairy to manage automatically. Seriously, though: don’t do it unless you absolutely must.

So there you have it: a quick and dirty category on NSObject to run arbitrary code at deallocation. I don’t really see a use it for it in production code, but on those occasions when you’re debugging someone else’s memory management, this could be handy. Since it uses blocks and associated objects, you’ll need to be running Mac OS X Snow Leopard (64-bit) or later or iOS 4.0 or later.

iOS Programming for Multicore Processors

With the impending release of the iPad 2, understanding how to program multithreaded applications will quickly become paramount as applications continue to push the envelope to make immersive experiences with high-performance computation. Now, without actually having a multicore iPad in my hands, I can’t say exactly how the system will behave, but there are a few best practices we should all be aware of when writing iOS code:

  • Using Core Data? You can’t share access to an NSManagedObjectContext across multiple threads, dispatch queues, or NSOperation queues, so for each one you’ll need to create a new instance. Similarly, don’t pass an NSManagedObject or subclass thereof between threads. Give each of your objects a unique ID—CFUUID works well for this—and pass the ID around, pulling a new object out of your NSManagedObjectContext for each thread. It’s a pain, but that’s how to (safely) get around threading and Core Data.
  • Always call UIKit updates from the main thread. Whatever you’re doing to your user interface, be it updating a label, loading an image into an image view, anything that’ll be rendered to screen—and some things that won’t—should be run on the main thread. There are two main ways to do this:
    1. Use Grand Central Dispatch. Using dispatch_get_main_queue(), you can get a reference to the main queue and submit blocks to it for updating your UI. This is typically a clean, easy way to refactor existing code for thread-aware programming.
    2. Use -performSelectorOnMainThread:withObject:waitUntilDone: and friends. This has the drawback of only working for methods that take one or zero Objective-C objects as arguments, but can be a quick and easy way to use fire-and-forget methods like -reloadData on UITableView.
  • Think about how you declare your properties. How many people have been using atomic properties? Before now, not many. In fact, before now, it was typically useless, as the chances of something interrupting your accessor methods was pretty low. Now, though, if you’re planning on accessing an object from multiple threads, you really need to control access to your properties.
  • Use locks. Locks, long the scourge of the multithreaded-code author, are simply essential for some parts of multithreaded programming. Whether you’re using NSLock or a lower-level lock, or even something like Grand Central Dispatch’s counting semaphore type, dispatch_semaphore_t, protect critical regions of your code from multiple accessors with (carefully-thought-out) locked access.

This list is by no means exhaustive, and Apple can do a lot to make this irrelevant (such as make UIKit threadsafe, which would be a killer iOS 5.0 feature), but with the iPad 2’s arrival, developers can no longer assume safety from threading problems. Be sure also to read Apple’s Threading Programming Guide to get anything I’ve left out.

It also makes a great excuse to buy an iPad 2. I mean, you need to test this, right?

Cocoa Touch Tutorial: Using Grand Central Dispatch for Asynchronous Table View Cells

One of the problems that an iOS developer will often face is the performance of table view cells. Table view cells are loaded on-demand by the UITableView that they’re a part of; the system calls ‑cellForRowAtIndexPath: on the table view’s dataSource property to fetch a new cell in order to display it. Since this method is called (several times) while scrolling a table view, it needs to be very performant. You don’t have very much time to provide the system with a table view cell; take too long, and the application will appear to stutter to your users. This kills the immersion of your application and is an instant sign to users that the application is poorly-written. I guess what I’m saying is that this code needs to be fast. But what if something you need to do to display the table view cell takes a long time—say, loading an image?

In my MobiDevDay presentation a couple of weeks ago, I illustrated a solution to this problem: Grand Central Dispatch. GCD, Apple’s new multiprocessing API in Mac OS X Snow Leopard and iOS 4, is the perfect solution for this problem. Let’s take a look at how it works.

Grand Central Dispatch operates using queues. Queues are a C typedef: dispatch_queue_t. To get a new global queue, we call dispatch_get_global_queue(), which takes two arguments: a long for priority and an unsigned long for options, which is unused, so we’ll pass 0ul. Here’s how we get a high-priority queue:

dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);

It’s pretty straightforward. To use this queue, we add blocks of code onto it. Typically this is done with blocks (Apple’s new code encapsulation extension to the C language), though it can be done with C functions. To submit a block onto a queue for execution, use the functions dispatch_sync and dispatch_async. They both take a queue and a block as parameters. dispatch_async returns immediately, running the block asynchronously, while dispatch_sync blocks execution until the provided block returns (though you cannot use its return value). Here’s how we schedule some code onto a queue (we’ll assume this code runs after our previous example, so queue is already defined):

dispatch_async(queue, ^{
    NSLog(@"Hello, World!");
});

It’s very easy to forget the ); at the end of that line, so be careful.

How does this apply to table view cells? Let’s take a look at a typical scenario for loading images from disk:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"ExampleCell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                       reuseIdentifier:CellIdentifier] autorelease];
    }
    
    // Get the filename to load.
    NSString *imageFilename = [imageArray objectAtIndex:[indexPath row]];
    NSString *imagePath = [imageFolder stringByAppendingPathComponent:imageFilename];
    
    [[cell textLabel] setText:imageFilename];
    UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
    [[cell imageView] setImage:image];

    return cell;
}

The problem with that code is that creating image blocks until ‑imageWithContentsOfFile: returns. If the images are especially large, this is catastrophic. Modifying this code to use Grand Central Dispatch is simple:

- (UITableViewCell *)tableView:(UITableView *)tableView
         cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
    static NSString *CellIdentifier = @"Cell";
    
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
    if (cell == nil) {
        cell = [[[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
                                       reuseIdentifier:CellIdentifier] autorelease];
    }
    
    // Get the filename to load.
    NSString *imageFilename = [imageArray objectAtIndex:[indexPath row]];
    NSString *imagePath = [imageFolder stringByAppendingPathComponent:imageFilename];
    
    [[cell textLabel] setText:imageFilename];
    
    dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);

    dispatch_async(queue, ^{
        UIImage *image = [UIImage imageWithContentsOfFile:imagePath];
    
        dispatch_sync(dispatch_get_main_queue(), ^{
            [[cell imageView] setImage:image];
            [cell setNeedsLayout];
        });
    });
    
    return cell;
}

First, we create our image asynchronously by using dispatch_async(). Once we have it, however, we have to come back to the main thread in order to update our table view cell’s UI (all UI updates should be on the main thread, unless you like reading crash reports). GCD has a function to get the main queue—analogous to the main thread—called dispatch_get_main_queue(). We can dispatch a block to that thread to update the UI.

By making this simple modification, we can very easily improve the performance of our table view. There are a few steps remaining, however, and this method has one serious shortcoming: if the cell is re-used by the time the image loads, it can load the wrong image into the cell. To get around this, it would be better to cache the images in an array or a dictionary (just be sure to release it in your view controller’s ‑didReceiveMemoryWarning: method). That said, this is an example of something you can do quite easily to improve the performance of your application. The better it performs, the more your users will like it, and that’s the ultimate goal.

The code used in this post is available as a GitHub repository.

MobiDevDay Presentation Slides

I gave a presentation on blocks and Grand Central Dispatch today at MobiDevDay. You can download the slides at SlideShare.

Looking for more reading about blocks? Here are some more resources:

Also, as we saw in the presentation, check out cdecl.org to cheat!

UPDATE: Looking for example code? I’ve put some of the code that went into the slides up on GitHub. It’s light, but it also includes a .PDF version of the presentation.

Selling “Physical Goods” or “Goods and Services Used Outside of the Application” in an iOS app

Since this question has come up a few times, I thought I’d record my answer to a common question:

Can I use an iPhone app to sell something outside of the App Store?

Reading Apple’s rules for the App Store makes developers nervous. We’re walking on eggshells, hoping that our application will not run afoul of the myriad of regulations put forth by Apple for inclusion into the store. So, questions like this come up. I’ll reproduce my answer here:

Obviously, I am not a lawyer, but I think you’ll be OK. Here’s my interpretation of the three relevant rules from the developer guidelines (emphasis mine):

11.1 Apps that unlock or enable additional features or functionality with mechanisms other than the App Store will be rejected.

11.2 Apps utilizing a system other than the In App Purchase API (IAP) to purchase content, functionality, or services in an app will be rejected.

11.3 Apps using IAP to purchase physical goods or goods and services used outside of the application will be rejected.

The first rule prohibits you from unlocking anything inside of your app with something other than the App Store. This would prevent you from, say, making a game that downloads new levels from your server based on your membership to a website.

The second rule prohibits you from, say, making a game and enabling PayPal in it to unlock more levels. Apple wants you to use in-app purchase for that.

The third rule—and this is where it gets interesting—prohibits you from using in-app purchase in an application to buy “physical goods” or “goods and services used outside of the application.” Nowhere does it say, however, that you can’t use other purchasing systems.

With that third rule, I think what Apple is saying is this: anything that runs on the iPhone must be purchased through the App Store, and everything purchased in the App Store must run on iOS. For something like insurance, which isn’t new functionality in the app, I think you’ll be OK. This is absolutely worth an e-mail to Apple’s technical support staff, but if you look at Amazon’s app, you can purchase physical goods using Amazon’s checkout system.

I’d love to be proven right or wrong on this. If my reading is correct, then using an iOS app as a storefront is perfectly acceptable. If I’m reading this too charitably, then you’re better off making this type of application for a different platform.