Using Blocks: Understanding the Memory Management Rules

11 Jul 2009, 18:09 PDT

Introduction

I just finished uploading PLBlocks 1.0-beta2, which adds support for iPhoneOS 2.2, iPhone 3GS armv7-optimized binaries, and host support for PPC systems (release announcement). This seems like a good time a good time to continue with my ad-hoc series on developing with Blocks (previous post).

I've fielded a number of questions regarding the memory management rules surrounding blocks; This article should help you understand the basic rules necessary to use blocks successfully.

I won't delve too deep into the describing the actual block implementation, as that's a topic well-covered by Clang's Block Implementation Specification Even if you don't have time to read the implementation specification, it's important to note that blocks are really just a few functions and structures implemented for you by the compiler, coupled with a runtime to help with managing setup and tear down. You could implement the same thing yourself in pure C -- but it would be so verbose as to not be useful.

The Memory Management Rules

Apple provides a set of straight-forward memory management rules for Objective-C. I've endeavoured to provide an equivalent set of rules for blocks in C and Objective-C.

This is the single fundamental memory management rule:

The following rules derive from the fundamental rule, or cope with edge cases:

Block Ownership: Copy vs. Retain

All blocks have a valid class pointer, and appear to be Objective-C objects. According to the standard Objective-C memory management, to assume ownership of an object you must simply retain it. However, blocks differ from the standard Objective-C objects in at least one fundamental and very important way: blocks defined within a function are allocated on the stack, and will persist only until your function returns.

This is why it is necessary to copy a block, rather than retain it: If the block is stack allocated, it must be copied to a heap allocation to persist beyond the lifetime of the current stack frame. Once copied, the block will be heap allocated and persist like any other standard Objective-C object.

Thus, to ensure that you do not attempt to maintain ownership of a stack allocated block, you must always copy a block if you wish to reference it past the lifetime of your current function. As an optimization, if a block has already been copied to the heap copying will simply increment the block's reference count.

It should also be noted that there is one very real benefit to stack allocation of blocks: they are very cheap, and unless copied, require no allocations or cleanup.

Captured Variables: Copy vs. Reference

By default, local variables (variables of the "auto" storage class) are captured by your block as const copies, while global variables are accessed directly. The runtime automatically handles reference counting of captured blocks and Objective-C objects.

To allow you modify a local variable (rather than its copy), Apple has added the __block storage specifier. Any __block variables are accessed by referenced from within a block. This is best explained by example:

__block int i = 0;
int j = 0;
executeBlock(^{
    // Increments the stack allocated 'i' variable by 1.
    i++;

// 'j' is a const copy, and can not be modified from within the block / j++ });

If you copy a block, any captured __block variables will also be copied to the heap.

Further Reading

I hope these brief explanations have provided a reasonable introduction to memory management with blocks. For additional details, you may wish to review Jim Dovey's post on the life-cycle of blocks, and join the PLBlocks Mailing List for further discussion.

Blocks Examples: NSOperationQueue and UIActionSheet

04 Jul 2009, 18:24 PDT

Introduction

On Friday, Plausible Labs released PLBlocks, an SDK that allows you to immediately begin experimenting with blocks (also known as closures) on Mac OS X 10.5 and iPhone OS 3.0. While the original announcement included a very brief introduction to blocks, I thought it might be worthwhile to provide some concrete examples of using blocks in your own code.

Example Code

All the sample code and implementation classes for this article are available via my block_samples github repository. You may download the repository as an archive (no git required) by pressing the "download" button next to the repository name.

To get started with the PLBlocks SDK, check out the download and installation instructions on the project page. Please note that PLBlocks is still in beta.

NSOperationQueue

On Mac OS X and iPhoneOS, there are a variety of ways to schedule operations on a background thread. One method that's often used is calling -[NSObject performSelectorInBackground:withObject:] to execute a method in the background, and then -[NSObject performSelectorOnMainThread:withObject:waitUntilDone:] to provide the results to the primary thread.

This works, but it's not as convenient as it could be:

Using blocks -- and a few small extensions to NSOperationQueue and NSThread -- we can instead encapsulate this full exchange in one method, using a block to run the background operation, and a nested block to handle the response directly on the main thread:

- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
    // Allocated here for succinctness.
    NSOperationQueue *q = [[NSOperationQueue alloc] init];
 
    /* Data to process */
    NSData *data = [@"Hello, I'm a Block!" dataUsingEncoding: NSUTF8StringEncoding];
 
    /* Push an expensive computation to the operation queue, and then
     * display the response to the user on the main thread. */
    [q addOperationWithBlock: ^{
        /* Perform expensive processing with data on our background thread */
        NSString *string = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
         
        /* This is the "expensive" part =) */
        sleep(5);
         
        /* Inform the user of the result on the main thread, where it's safe to play with the UI. */
        [[NSThread mainThread] performBlock: ^{
            NSAlert *alert = [[[NSAlert alloc] init] autorelease];
             
            [alert addButtonWithTitle: @"OK"];
            [alert setMessageText: [NSString stringWithFormat: @"Processing completed: %@", string]];
            [alert runModal];
        }];
 
        /* We don't need to hold a string reference anymore */
        [string release];
    }];
}

The first block is scheduled to run on the NSOperationQueue, and inside contains an additional nested block. When the operation has completed, it schedules its nested block to run on the main thread, where the result can be presented to the user, or passed on for further processing.

You can find the full NSOperationQueue and NSThread extensions -- including example usage -- here.

UIActionSheet

If you've done any iPhone development, you'll know that using UIActionSheet is a bit complicated -- more so if you want to share an action sheet implementation across view controllers, or display multiple UIActionSheets from a single view controller.

If you've used UIActionSheet in the past, you've had to do the following:

Using blocks, we can significant reduce the effort required to define and use a UIActionSheet. Instead of defining a delegate, and matching index values to your button actions, we can simply pass a block that implements the button's action directly:

- (void) displaySheet {
    PLActionSheet *sheet = [[PLActionSheet alloc] initWithTitle: @"Destination"];
    
    /* A re-usable block that simply displays an alert message */
    void (^alert)(NSString *) = ^(NSString *message) {
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle: @"Destination Selected"
                                                        message: message
                                                       delegate: nil
                                              cancelButtonTitle: @"OK"
                                              otherButtonTitles: nil];
        
        [alert show];
        [alert release];
    };
    
    [sheet addButtonWithTitle: @"Work" block: ^{
        alert(@"Work selected");
    }];
    
    [sheet addButtonWithTitle: @"Home" block: ^{
        alert(@"Home selected");
    }];
    
    [sheet addButtonWithTitle: @"School" block: ^{
        alert(@"School selected");
    }];
    
    [sheet setCancelButtonWithTitle: @"Cancel" block: ^{}];
 
    [sheet showInView: self.window];
    [sheet release];
}

That's it -- there is nothing else. The blocks used for each button automatically have access to the enclosing method's variables, and we even use another block (alert) to avoid retyping the UIAlertView boilerplate or cluttering our class with an alert method.

You can find the full UIActionSheet wrapper -- including example usage -- here.

Conclusion

I hope you'll find these examples useful in experimenting with and incorporating blocks into your own software. There are quite a few other ways that blocks can be leveraged to decrease code size and complexity, and I'll plan on writing future articles on the subject.

If you'd like to discuss blocks, the PLBlocks implementation, or having any other questions, feel free to join the PLBlocks mailing list.

PLBlocks: Blocks for iPhoneOS 3.0 and Mac OS X 10.5

02 Jul 2009, 21:49 PDT

Over the weekend (and for some of the week) I've been working on back-porting block support from the Snow Leopard toolchain, with the goal of leveraging blocks for our iPhone and Mac OS X 10.5 development at Plausible Labs.

If you're unfamiliar with blocks, they're an implementation of closures for C, Objective-C, and provisionally C++. The full language specification and ABI specification are available from the LLVM/Clang project.

I've now published an initial beta release with support for iPhone OS 3.0 (armv6), and Mac OS X 10.5 (i386, x86-64, ppc) -- you can find the full announcement -- including download and usage instructions -- here.