Wednesday, April 4, 2012

Blocks instead of RAII

Many C++ programmers are familiar with the resource-aquisition-is-initialization pattern, where a class's constructor and destructor manage the acquiring and releasing of a resource, typically a lock such a mutex. Before blocks were added to Objective-C, I missed this pattern because I felt it made it so much easier to get things right and keep code maintainable. Blocks provide an equally clean way to factor the acquiring and releasing of a resource into reusable code that achieves the same goal, but rather than create a class, you create a function. 

For example, an OS X IOSurfaceRef must be locked before accessing the data with the CPU. The calls to lock and unlock the surface are simple enough, but why repeat them? Here's a function that does that clearly:

OSStatus CallBlockWithIOSurfaceLockedForReading(IOSurfaceRef surface, OSStatus (^block)(void))
{
    uint32_t seed = 0;
    OSStatus error = IOSurfaceLock(surface, kIOSurfaceLockReadOnly, &seed);
    if (!error)
    {
        error = block();
        
        OSStatus unlockError = IOSurfaceUnlock(surface, kIOSurfaceLockReadOnly, &seed);
        if (!error)
            error = unlockError;
    }
    return error;
}


And a call to that looks like:

CallBlockWithIOSurfaceLockedForReading(surface, ^OSStatus{
    void *bytes = IOSurfaceGetBaseAddress(surface);
    // do something with bytes here.
    return noErr;
});


This pattern ensures that you get consistent behavior wherever in your code you need to read data from an IOSurface, getting the appropriate error values, and always keeping the calls balanced, which is critical. 

No comments:

Post a Comment