Wednesday, April 18, 2012

PaintCode App Review

With the new iPad and its Retina Display, iOS apps are getting even larger due to the various sizes of assets. When a user downloads your app from the store, you want them to get it quickly. You especially don't want them to be forced to download from wifi if you can help it. Fortunately, a new app called PaintCode has been released just in time by PixelCut to help remedy this problem.

Summary

PaintCode is a vector image editor. What makes it special is that PaintCode generates Core Graphics code for both Mac OS X and iOS. The idea is that we can draw many of our graphics rather than include a series of rasters in the app's main bundle. PaintCode also serves as a bridge between graphics artists and developers. The graphics artist can generate the assets using PaintCode, then the developer can take the generated code and tweak it as needed.

Test Run

To really see if PaintCode could fill my needs, I stress tested it with a fairly complicated image: the Siri button. The Siri button touches nearly every feature of PaintCode, including arbitrary shapes, linear/radial gradients, and inside/outside shadows.


Within a few hours, I had all the intricate details worked out. I consider myself an amateur graphics artist, but PaintCode made the process very easy. Most of the controls are intuitive and easy to learn. The provided examples and videos also helped.

I then put the code to the test. I simply dropped the code into a custom UIView. To make things more interesting, I applied some affine transformations.


I found that the transformations worked perfectly with everything except, inside shadows. The inside shadows will translate properly, but won't rotate or scale properly. I am not entirely sure why they don't work. I expect this bug will be fixed in a future release.

Pros

Again, PaintCode made generating vector assets very easy. I tried out the trial of a competing app, Opacity, but I found PaintCode to be much more intuitive. I also appreciate the great attention to aesthetic detail, yet minimalistic style.

Everything in PaintCode is drawn using Core Graphics. So, whatever you see on the canvas is exactly what will appear in your own app.

The generated code may not be perfect, but it's pretty good. The advantage of PaintCode is it does the hard, tedious work. Once the image is finished, a developer can easily tweak details as needed to make the drawing more dynamic.

Cons

Though PaintCode generates some good Core Graphics code, it is not truly resolution independent. You will notice this particularly with the radial gradient on the button. I wanted to alternate colors about once every pixel or two. PaintCode only allowed 21 discrete steps for a gradient. Furthermore, since the number of steps is fixed, changing the scale changes the appearance. Fortunately, this can be remedied by tweaking the generated code. A simple for loop can dynamically change the number of steps as the scale changes.

As any graphics artist knows, vector images are not a silver bullet. There are some relationships that are not 1:1 linear transformations. For example, one object may scale twice as fast as another. The scale could even be x2. This scaling problem can be fixed in code though. Also, when the size changes, details often need to be added or removed. The code could be modified to do this automatically as the scale changes, but this is probably outside the scope of PaintCode and may be more trouble than it is worth.

My Wishlist

If there is anything I could have added to this app, it would be import SVG. Every graphics artist has his or her favorite vector editor. It would be amazing if PaintCode could import an SVG file (or AI file) and convert it to Core Graphics code. I have no doubt such a feature would be a large undertaking, but if PaintCode could do this, it would be worth so much more than $80.

Recommendation

If you have an app that is getting large, you should really look into getting PaintCode and converting some of your assets to Core Graphics. Or if you are trying to draw something in Core Graphics and find debugging too hard, PaintCode will make save you many hours.

Code

If you want a copy of my PaintCode file or Xcode project, you can find them on my GitHub account.

Disclaimer

I received a copy of PaintCode free for the purposes of reviewing. However, I do not receive any compensation if you purchase PaintCode.

Tuesday, March 6, 2012

Hidden iOS Simulator Hotkeys

Apple's iOS Simulator app is a helpful tool in building iOS apps. However, there is more than what appears on the surface. Recently a hack has been discovered to allow iPad Retina Display testing. I have also found a couple hidden features. As far as I know, these hidden hotkeys only work on the iOS 5 simulator.

The first, and least interesting, is the "Toggle Slow Animations" feature. This feature slows down animations making it easier to see the fine details. I've found this useful when "debugging" animations. This feature is normally toggled through the drop-down menu: Debug > Toggle Slow Animations. However, it can also be toggled by double tapping Cmd+Shift. If you ever have slow animations mysteriously turn on, then you may have accidentally activated this hotkey.

The second hotkey is more peculiar. I often use the hotkey Cmd+Shift+4+Space to take screenshots of the simulator. I noticed that sometimes a popup would show up that displays the keyboard localization languages I have turned on. It took a while, but I can almost consistently reproduce it. I first disabled the screenshot hotkeys:  (Apple) > System Preferences > Keyboard > Keyboard Shortcuts > Screen Shots > Uncheck all hotkeys. Then I opened the iOS simulator and used the hotkey Cmd+Shift+4+Space. Sometimes it takes a few tries. Other times I need to open/close an app and try again. Again, this hotkey only appears to work on the iOS 5 Simulator. You may also need to have more than one keyboard language activated.


When the keyboard language popup shows up, you can click on of the languages to change your keyboard language. The effect is not immediately apparent unless you have the keyboard visible while changing languages.

I'm not entirely sure why this hidden feature exists in the simulator. If the multiple keyboards are setup, they can be easily changed using the language key on the keyboard. What is more interesting is Apple likely has many other hidden features.

Friday, January 13, 2012

UIStoryboard Best Practices

UIStoryboard is a hot topic right now in iOS. However, there have been many misconceptions on the topic. The first reaction to storyboards (what I have seen anyway) is that they are a panacea for all situations. Following that reaction I saw many people claim that storyboards are awful and broken. The truth is, both these ideas are wrong. During WWDC 2011 Apple may have over-promoted storyboards, thus giving the wrong impression and leading to unmet expectations. Let me correct this now: UIStoryboard is not an all-purpose solution, it is another tool for your toolbox.

My intent here is to provide the best practices for UIStoryboard. I will list my lessons learned, but there are probably more yet to learn. I expect over time that this list will expand to include other best practices for UIStoryboard.

Break your storyboard into modules

The first mistake developers have been making with storyboarding is producing large, monolithic storyboards. A single storyboard may be suitable for small apps but certainly not for large apps. Don't forget one of the key principles of programming and abstraction: decomposition. We decompose our code into modules to make them more reusable and reduce maintenance costs. The same is true for user interfaces, and it applies whether you are using UIStoryboards, XIBs, or anything else. There is an even deeper principle wanting to emerge: when code and user interfaces are decomposed together, then you have truly useful and reusable code, user interfaces, and products (you can quote me on this).

The first question to ask is "how do I identify the natural modules of my app?" Let's start with something easy. Many apps are based on a tab bar controller. Each tab is a natural module and you hence you can put them in their own storyboard. Next, if you have two tabs (or any two views for that matter) with a segue to a common view, then that common view (and its following hierarchy) is almost certainly another natural module. Once you have pruned your view hierarchy, you will probably find that each module can be given a name. For example, you may have your login storyboard, settings storyboard, about storyboard, main storyboard, etc. If you can give a fitting name to each module, then you have decomposed your hierarchy well.

Keep in mind that having a single view in a storyboard is not a bad thing. This is particularly true with table views. The power of static and prototype table view cells is very useful.

You should also note that breaking storyboards into modules makes them more friendly to version control and sharing among a team.

You don't need to convert everything at once

When you first saw storyboards, you may have had an urge to convert everything over to UIStoryboard. I know I did. Be aware that this does take time and you may not gain any immediate benefits from switching everything. Fortunately, you don't need to do it all at once. You can switch over the parts of your app that will benefit the most now. Then over time you can slowly switch out others.

Make custom UITableViewCell subclasses for prototype cells

Probably the best feature of UIStoryboard is prototype cells. They make custom table view cells easy. Prototype cells can be easily customized; however, the UITableViewCell class may not specify the IBOutlets you need. A custom subclass also gives you another advantage. You can create a -setupWith<#Object#>: method. This takes your setup code out of your table view controller and into the cell itself. This is where you can gain some great reuse.

Not only can you create custom UITableViewCell subclasses, but you can have one subclass service many prototype cells. For example, you may have a cell that takes a Person object. In one table view your cell may have main label on the left and a detail label on the right. A different table view may use the same cell but have a main label above and a detail label below. Your cell subclass handles the content, but the prototype cell handles the look and feel. Again we gain great reuse and flexibility by decomposing code and views together.

Don't forget about IBAction, IBOutlet, and IBOutletCollection

Segues are nice and can do a lot, but don't forget about the other IB fundamentals. Segues can't cross storyboards, but IBActions can. IBActions can also clean up an unnecessary spaghetti segue mess and reduce the need for custom segues. Everything has their place and their use; they are all tools at your disposal.

While on the topic of IBActions and family, I should cover encapsulation. Most, if not all, your actions and outlets do not need to be public. So, rather than put them in your public headers, put them in a class extension. See here for more details. Xcode/Interface Builder didn't handle this well in the past but now there are no problems. This keeps your interfaces much cleaner and easier for others to understand.

Don't forget about XIBs

Even though storyboards are very similar to XIBs, they still have a place. Simple, one-view modules may be better represented as XIBs. Also, XIBs can hold custom views without an associated view controller. UIStoryboard requires that view controllers be the basic unit.

You don't need to have a main storyboard

You may have noticed your app settings now has a "Main Storyboard" setting. To use UIStoryboard you are not required to use this. In fact, my latest project has neither a main storyboard nor a main XIB. It uses a programmatic UITabBarController. From there I load all my tab storyboards. The beauty of it all is that it doesn't matter whether I use UIStoryboards, XIBs, or code. All three options are flexible enough to work with each other in any combination.

Specify the name of your app delegate in main.m

As I mentioned the other day, you may need to change your main.m file if it doesn't specify your app delegate's name. It should look something like this:

UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class]))

Put your storyboards in a category

To get one of your storyboards, you need to call +storyboardWithName:bundle:. Be wary any time you need to hard code a string for a key, especially one that can change frequently. Should you ever need to rename your storyboard, you will have to change all references to that storyboard. My favorite way to handle this is with a category. It looks something like this:

@implementation UIStoryboard (MyApp)

+ (UIStoryboard *)mainStoryboard {
    return [UIStoryboard storyboardWithName:@"MainStoryboard" bundle:nil];
}

@end

The benefit of the category approach is that if you ever rename a storyboard, you only need to change the hard coded string in one place. It also makes your code more readable.

Summary

If you take anything from this post you should remember these:
  1. Decompose your code and your views.
  2. UIStoryboard is a great tool, but it's not the only tool.
I hope these tips and best practices help promote proper use of UIStoryboard. I invite all my readers to let me know of other best practices they encounter.

Thursday, December 15, 2011

Migrating to UIStoryboard

I’ve just recently started using UIStoryboard and so far I’ve been very pleased with it. Building the UI is faster, easier, and requires less code. It’s also immensely helpful to get a bird’s eye view of your full workflow. However, when migrating an old app from using regular XIBs to UIStoryboard I did encounter some troubles. When launching the app, I would get the following error messages:

“The app delegate must implement the window property if it wants to use a main storyboard file.”
and
“Applications are expected to have a root view controller at the end of application launch.”

I could clearly see that my app delegate was implementing the window property as expected. Fortunately, StackOverflow came to the rescue. It turns out that main.m needs to be modified when switching to UIStoryboard from XIBs (in older projects). The change looks like this:

// Old main.m
UIApplicationMain(argc, argv, nil, nil);

// New main.m
UIApplicationMain(argc, argv, nil, NSStringFromClass([MyAppDelegate class]));

The documentation for this parameter reads:

The name of the class from which the application delegate is instantiated. If principalClassName designates a subclass of UIApplication, you may designate the subclass as the delegate; the subclass instance receives the application-delegate messages. Specify nil if you load the delegate object from your application’s main nib file.

Basically, if you aren’t using XIBs, you must let UIApplication know what your app delegate’s name is. You will also notice that any new projects created with the latest version of Xcode will specify the app delegate, even if you are using XIBs. This prevents any problems should you migrate to UIStoryboard later.

UIColor Category

For a long time I’ve wanted more convenience methods for UIColor. The set Apple provides is a nice starting point but I often find myself wanting more. In particular, the Mac color picker has a crayon box color picker with common colors. Sadly, there are no convenience methods for these colors. Since I use them so frequently, I decided to put them all in a category. This was tedious work since I had to do them one-by-one by hand.

After getting these 48 convenience methods I figured why stop there. I moved on to the HTML colors. At the time I didn’t realize how many standard HTML colors exist (147). After seeing that number I almost threw out the idea, but I remembered that @alesplin taught how to use Vim macros in the November BYU CocoaHeads meeting. Using a list of HTML colors and their associated hex values, I had all 147 HTML color convenience methods finished within minutes. Vim may be an old, crusty text editor, but its macro editing power is unmatched by modern IDEs.

If you want a copy of my UIColor category, you can find it in my RBCategories repo on Github.

Wednesday, November 30, 2011

Read/Write Lockless Exclusion

With the recent release of iOS 5, GCD now has a simple and elegant solution to the reader writer problem that utilizes lockless exclusion. In summary, this involves using dispatch_async (or dispatch_sync) to enqueue "reader" blocks onto a user concurrent queue. These blocks may be used to read some kind of data but not write to it. If we need write access, then we can use the new dispatch_barrier_async (or dispatch_barrier_sync) function. This will put a special barrier block on the queue. The blocks are still dequeued in FIFO order. However, when there are one or more reader blocks running, then a writer block may not be dequeued. While a single writer block is running, no other blocks may be dequeued, including other writer blocks. This guarantees that writer blocks have exclusive access and reader blocks may share access.

The new barrier blocks and user concurrent queues are immensely valuable. Lately, I've been considering how they relate to my recently developed Lockless Exclusion Accessor (LEA) design pattern. If you are not already familiar with my LEA pattern, you should first read my LEA article. The intent of a LEA is to make mutual exclusion easy and efficient. Since we want to be efficient, let's push efficiency further by allowing shared read access. Here is some generic code that does just that:

/// Block type to use when accessing the shared memory.
/// The type is 'id' just to be generic. With a real
/// LEA, you should use the exact type.
typedef void(^LEABlock)(id sharedMemory);

/// Class extension for holding the private, shared memory
/// and its associated queue.
@interface MyObject ()

@property (nonatomic, strong) id sharedMemory;
@property (nonatomic, assign) dispatch_queue_t leaQueue;

@end

@implementation MyObject

@synthesize sharedMemory = _sharedMemory;
@synthesize leaQueue = _leaQueue;

- (id)init {
    if ((self = [super init])) {
        // The queue MUST be concurrent to gain shared readership.
        dispatch_queue_t queue = NULL;
        queue = dispatch_queue_create(
          "com.RobBrown.LEAQueue",
          DISPATCH_QUEUE_CONCURRENT);
        [self setLeaQueue:queue];

        // Initialize shared memory here.
    }
    return self;
}

- (void)accessSharedMemoryReadWriteAsync:(LEABlock)block {

    // Block must not be nil.
    NSParameterAssert(block);

    // Executes the block asynchronously with exclusive access.
    dispatch_barrier_async([self leaQueue], ^{
        block([self sharedMemory]);
    });
}

- (void)accessSharedMemoryReadOnlyAsync:(LEABlock)block {

    // Block must not be nil.
    NSParameterAssert(block);

    // Executes the block asynchronously with shared access.
    dispatch_async([self leaQueue], ^{
        block([self sharedMemory]);
    });
}

@end

The first thing to note is that the dispatch queue is now concurrent instead of serial. If you use a serial queue then you will have no added benefits. The next thing to note is that every previous LEA in your object splits in two: a readonly version and a read write version. It's only four extra lines for a potentially great increase in efficiency.

As always, you should favor dispatch_async over dispatch_sync. If you must have synchronous access, then you can add synchronous variants too. You must, however, never use my dispatch_sync_checked function. It is not and never will be compatible with barrier blocks.

By introducing the readonly LEA, we now introduce a potential point of failure. What if we write some code that uses a readonly LEA, then we later change the code to modify the shared memory without switching to a read write LEA? This could introduce race conditions. If needed, you can add a sanity check to your readonly LEA. Here are a few options:
  1. If the object you are passing back has mutable and immutable variants, create two block types. The read write LEA will use the mutable variant and the readonly LEA will use the immutable variant. This is the best solution.
  2. If you don't have mutable and immutable variants, then you can simply pass a copy of the shared memory to the readonly block. This can potentially mask errors since you won't know if you accidentally use a readonly LEA when you meant to use a read write LEA.
  3. Before calling the block, you copy the shared memory. Then after calling the block, you check that the shared memory was not changed by comparing it to the earlier copy. If it was changed, then throw an exception. Although Objective-C rarely throws exceptions, this is an appropriate place to do so. Overall, this solution can be tedious work. You should also use a pre-processor macro to remove this check from release builds for efficiency.
  4. You can create a subclass of NSProxy. Instead of passing back the shared memory you can pass it the proxy. This proxy should throw an exception if any setters are called. This too can be tedious work. You may want to use a pre-processor macro here as well to remove the proxy in release builds.
Don't forget that user concurrent queues and barrier blocks are iOS 5 (and Mac OS X Lion) only. If you are using iOS 5 and you need mutual exclusion, I highly recommend using Read/Write LEAs (or at least regular LEAs). They make mutual exclusion fast and easy.

Monday, November 28, 2011

dispatch_sync_safe Deprecated

A while back I wrote a pseudo-category for GCD and posted it to Github. It contains several convenience functions. One of them was a method that makes synchronous calls “safe,” or so I thought. The code looked like this:

void dispatch_sync_safe(dispatch_queue_t queue, dispatch_block_t block) {
    // If we're already on the given queue, just run the block.
    if (dispatch_get_current_queue() == queue)
        block();
    // Otherwise, dispatch to the given queue.
    else
        dispatch_sync(queue, block);
}

The idea is that when doing a synchronous dispatch to a queue, I check if the program was already running on that queue. If it is, then the block is immediately executed. Otherwise, the block is run with a regular dispatch_sync. In my naïve implementation I thought this would prevent deadlock. The idea is nice but not correct. Consider the case when we dispatch_sync_safe to queue A. Next, we dispatch_sync_safe to queue B. Finally, we dispatch_sync_safe back to queue A. This is guaranteed deadlock.

As a result of this case, I immediately marked dispatch_sync_safe as deprecated in my Github repo. Anyone using this function thinking it is perfectly safe should think again. I did, however, add another function called dispatch_sync_checked. It is actually the same implementation as dispatch_sync_safe just under a different name. I recognize that this simple function has great value, but the old name was misleading. The moral of the story is, use dispatch_async (or one of my dispatch_async convenience methods) whenever possible. If you must run code synchronously, use dispatch_sync_checked and be very cautious of deadlock.

As I mentioned previously, dispatch_sync_checked still has great value. There is at least one case where you can guarantee you won’t deadlock. Consider the case when you have an object with a private serial dispatch queue. This queue is used to serialize access to some shared memory within the object. Using dispatch_sync_checked on the private queue allows you to keep your object’s methods synchronous and thread safe. This requires following two conditions in your critical code:
  1. Do not make synchronous calls to external objects (which could synchronously call the calling object again).
  2. Do not call dispatch_sync on any queue (or dispatch_sync_checked on any queue except the private queue).
Also, since these methods check for running on the private queue, they can call each other without worrying about deadlock. This gives similar behavior to a recursive lock without the overhead. If @synchronized(self) { … } just isn’t fast enough, consider switching to dispatch_sync_checked(_queue, ^{ … }).

Monday, October 17, 2011

My Philosophy on Language

When I was called to serve a mission in Canada for The Church of Jesus Christ of Latter-day Saints, I was called to English-speaking assignment. During my mission, I spent six months in a French-speaking assignment since I had taken three years of French in high school. After serving in that assignment for quite some time, I found that my thoughts had become detached from all language. This skill was useful (and probably resulted) because I spent all day switching between French and English. When I thought of something, I would mentally envision the object or idea rather than thinking of the English or French equivalent. This way I could mentally transition between languages without any loss of context.

I think language can limit our mental capacities. If we all view the world in the context of our native language, then we can only express ideas that already exist in that language. Language, however, is necessary so we can communicate our thoughts to others. Ironically, I must write this short article in a language, which makes it somewhat difficult to express such an abstract idea.

So, how does this relate to computer science? Programming languages are just as much languages as the natural languages, such as English or French. The difference is that programming languages can express different ideas from natural languages. Like natural languages, not all programming languages can express the same ideas. This may limit what you as a developer can mentally design and express. For example, if your language doesn’t support blocks, then you probably don’t know what continuations are. One reason that I like Objective-C so much is that it allows me to express many ideas freely, especially when playing with the dynamic runtime.

Likewise, if we only think in the context of our favorite programming language(s), then we can only develop ideas that already exist in that/those languages(s). By becoming detached from all languages (both natural and programmatic) we open ourselves to think of ideas that don’t exist yet. Once we create a new idea, we are then obligated to express that idea in a language or change our language to allow us to express that idea to other people. Again, the language we express our idea in can be natural or programmatic.

By the way, my ideas on language have a direct connection to The Five Orders of Ignorance by Philip Armour. Briefly, Armour argues that in order to ask questions and find answers about some idea, we must first develop a framework for thinking about that idea. I think that language is indirectly, and often directly, related to developing mental frameworks for thinking.

Thursday, October 6, 2011

A Tribute to Steve Jobs

The world is mourning the loss of a great man. I just hope that his great vision has not been lost as well. Steve lived his dreams. He was never content and continually pushed the boundaries of technology. His quest for perfection led to unparalleled products and user experiences. Perhaps it is best said of him what was said back in 1997:

Here’s to the crazy one. The misfit. The rebel. The trouble-maker. The Square peg in the round holes. The one who saw things differently. He wasn’t fond of rules, and he had no respect for the status quo. You can quote him, disagree with him, glorify, or vilify him. About the only thing you can’t do is ignore him. Because he changed things. He pushed the human race forward. And while some may see him as the crazy one, we see genius. Because he was crazy enough to think he could change the world, and he did!

Thank you for everything Steve Jobs. Because of you I have a job I love. The technology you developed has made life better and the world more connected. Through your example, others have strived to push their limits, including me. Rest in peace Steve and may God watch over your family.

Saturday, September 17, 2011

Lockless Exclusion Accessor Pattern

With all the work I have been doing with blocks and Grand Central Dispatch (GCD), I have developed a useful design pattern for handling mutual exclusion. I haven’t seen any patterns quite like this one, so I’ll claim it as my own.

Mutual exclusion is always a challenge when multithreading any application. GCD makes mutual exclusion substantially easier by using queues instead of locks. Nevertheless, there are still some challenges. When creating shared data I often include a warning in my documentation that it must only be accessed within a certain serial GCD queue. However, other developers that use my code may not read my documentation. Even I forget occasionally that some particular data isn’t thread safe. So, how do we make this easier? This is a situation for what I call the Lockless Exclusion Accessor pattern. I’ll call it LEA for short. (I also thought of calling it a “letter” since it’s closely related to a getter, but that seemed too silly and generic.)

Before going into the details of LEAs, we need to first understand why encapsulation is important. Most object-oriented languages have the ability to make variables private so they can only be directly accessed internal to the class. Limited access is given to the outside through getters and setters. This prevents classes on the outside making arbitrary changes to another classes internal data. To see how to encapsulate @propertys in Objective-C check out my @property article. We will apply the same principles to our shared data.

First, move your shared data into a class extension so it will not be externally visible. Second, you will want to typdef your block type. This keeps your code cleaner and easier to read. For this example, I’m going to use an NSManagedObjectContext as my shared data. So, my block typedef looks like this:

typedef void(^RBMOCBlock)(NSManagedObjectContext * moc);

Next, you need to create an accessor method. Outside classes will only be able to access your shared data through this method. It will look something like this:

- (void)accessMOCAsync:(RBMOCBlock)block {
        dispatch_async(dispatch_get_main_queue(), ^{
                block([self managedObjectContext]);
        });
}

The dispatch_async takes the passed in block and runs it on the main queue. We could (and really should) modify this code to run on a private serial queue instead. We also pass the shared data to the block. This grants the block exclusive access to that data (as long as the dispatch queue is a serial queue). You’ll also notice that this is an asynchronous call. If needed, we can also make a synchronous LEA. It looks nearly identical:

- (void)accessMOCSync:(RBMOCBlock)block {
        dispatch_sync(dispatch_get_main_queue(), ^{
                block([self managedObjectContext]);
        });
}

Now that we’ve built our asynchronous and synchronous LEAs, let’s use one.

[object accessMOCAsync:^(NSManagedObjectContext * context) {
        // Critical code goes here.
}];

That’s all there is to it. Just wrap your critical code in a block and you get exclusive, local access to the NSManagedObjectContext. LEAs are perfect for Core Data MOCs since they are not thread safe and are often the cause of much grief.

LEAs are also quite flexible. For example, you can merge multiple LEAs into one like this:

[object accessMOCAsync:^(NSManagedObjectContext * context, NSDictionary * userInfo) {
        // Critical code goes here.
}];

The above LEA gives the block exclusive access to two shared variables at once. You can extend this as far as you want. This way to don’t need to write multiple LEAs per class and you don’t need to nest LEAs. However, this comes with a tradeoff. You gain convenience at the cost of more contention for shared data.

Now, LEAs don’t perfectly protect shared data. It’s as good as Objective-C can get though. Rogue developers could pass the pointer to the MOC out of the block. They could also use the dynamic runtime to call the internal getters and setters. However, any developer doing either of these things is asking for a lot of trouble and should not be permitted to write Objective-C code.

For more GCD and block design patterns, check out these sources.


UPDATE:

I've removed all references to SyncSafe due to problems I've discovered.