I stumbled across a nice article by Jonathan Dann about using blocks to improve transactional code.
When I say "transactional" code, I mean code that has an opening, a middle, and a closing, where the opening and the closing have to be matched. You see it in a few places in Cocoa, such as:
- KVO:
- willChangeValueForKey:
- change the value
- didChangeValueForKey:
- NSGraphicsContext:
- saveGraphicsState
- do some drawing
- resoreGraphicsState
- NSFileWrapper:
- open a file
- read/write to it
- close the file
I’m going to show you a "before and after" comparison of a function that uses NSGraphicsContext and NSAffineTransformation.
The function DrawCrossedFlags takes an upright flag (drawn by DrawFlag), and draws it twice to make two crossed flags at the point (100,100) like this:

The first example is the DrawCrossedFlags code without blocks. To make the comparison more fair, it uses unorthodox indenting and custom class methods on NSAffineTransform.
//Example 1. Without blocks void DrawCrossedFlags() { [NSGraphicsContext saveGraphicsState]; [NSAffineTransform translateXBy:100.0 yBy:100.0]; [NSGraphicsContext saveGraphicsState]; [NSAffineTransform rotateByDegrees:45.0]; DrawFlag(); [NSGraphicsContext restoreGraphicsState]; [NSGraphicsContext saveGraphicsState]; [NSAffineTransform rotateByDegrees:-45.0]; [NSGraphicsContext saveGraphicsState]; [NSAffineTransform scaleXBy:-1.0 yBy:1.0]; DrawFlag(); [NSGraphicsContext restoreGraphicsState]; [NSGraphicsContext restoreGraphicsState]; [NSGraphicsContext restoreGraphicsState]; }
It’s hard to tell where the saveGraphicsState and restoreGraphicsState calls match up. It would be even harder if I had used normal indenting. However, if we enter the magical world of closures, it can be written like this:
//Example 2. With blocks void DrawCrossedFlags() { [NSAffineTransform withTranslationX:100.0 andY:100.0 draw:^{ [NSAffineTransform withDegreesRotated:45.0 draw:^{ DrawFlag(); }]; [NSAffineTransform withDegreesRotated:-45.0 draw:^{ [NSAffineTransform withScaleX:-1.0 andY:1.0 draw:^{ DrawFlag(); }]; }]; }]; }
The second example cuts down the amount of code, which is always a good thing. Also, as a side effect of using blocks, the natural indentation immediately lets you know where the transformations start and end. Here is the implementation of +[NSAffineTransform withDegreesRotated:draw:] so that you can see under the hood:
+(void) withDegreesRotated:(CGFloat)degreesRotated draw:(void(^)())drawBlock; { [NSGraphicsContext saveGraphicsState]; NSAffineTransform* t = [NSAffineTransform new]; [t rotateByDegrees:degreesRotated]; [t concat]; [t release]; t = nil; drawBlock(); [NSGraphicsContext restoreGraphicsState]; }
Jonathan Dann has written similar categories for the following classes:
- NSGraphicsContext
- CGContextRef
- NSManagedObject (for writing your custom accessors)
- NSObject (for KVO)
- CATransaction
- NSAnimationContext
- NSTextStorage
If you want his code (New BSD license), you can get it from his article.
If you enjoyed this post, why not subscribe?
