Block 实用指南

Mike Ash Friday Q&A 中文译文:Block 实用指南

作者 TommyWu
封面圖片: Block 实用指南

译文 · 原文: Friday Q&A 2009-08-14: Practical Blocks · 作者 Mike Ash

原文:https://www.mikeash.com/pyblog/friday-qa-2009-08-14-practical-blocks.html 发布:2009-08-14 作者:Mike Ash 译者:MiMo(mimo-v2.5-pro);代码块保留英文原样


欢迎回来阅读新一期的 Friday Q & A。休假归来后,我已准备好为你带来更多编程好料。本周我打算采纳 Landon Fuller 的建议,在 blocks(块)的设计最终确定且代码已可获取后,撰写一篇我最初关于 blocks 的 Friday Q & A 的后续文章。

尽管苹果尚未在其任何开发工具中正式搭载 blocks 功能,但由于其参与了开源编译器 gcc(GNU 编译器套件)和 clang 的开发,他们已发布了其 blocks 实现的代码。Landon 利用这段代码整合出了 PLBlocks,该工具允许在 Mac OS X 10.5 上构建和运行基于 blocks 的代码。虽然在 10.5 系统上没有基于 blocks 的 API(除了作为 runtime(运行时)一部分的最基础功能外),它们仍然能发挥显著作用。(译注:Mac OS X 10.5 是较旧的操作系统版本,现代系统中 blocks 已成为标准特性,而 gcc 在此上下文中现多指代历史编译器工具链。)

我不会在这里介绍 PLBlocks 的安装或基本用法,因为 PLBlocks 的页面已非常详尽地涵盖了这些内容。如果你想跟着操作,请前往该页面并按照指引进行。关于 blocks 如何工作的更多信息,请参阅 Clang 的 blocks 语言规范及实现规范。

我也会假设你了解 block 语法和用法的基础知识,这些内容在我上一期的 Friday Q & A 中已有涵盖。今天的主题本质上是上一篇的续篇。如果你还没读过,请先阅读上一篇。

基础 Blocks 是 Objective-C 对象。当你在代码中编写一个 block 时,这是一个表达式,其类型是对象类型,这非常类似于 @"..." 常量字符串语法会给你一个对象类型的表达式。然后你可以像使用任何其他 Objective-C 对象一样使用这个 block,比如向它发送它能响应的消息、将其放入容器、作为参数传递、返回它,等等。

这里有一个与常量字符串语法的主要区别。与常量字符串不同,blocks 每次在代码中出现时并不是完全相同的。这是因为 blocks 会捕获其封闭作用域(enclosing scope),而该作用域在每次调用时都不同。简而言之,每次代码执行遇到一个 ^{...} 结构时,都会创建一个新对象。

每次分配新对象会相当慢,所以块采用了一种特殊方式:通过 ^{...} 结构体获得的对象是一个栈对象。这意味着它与局部变量具有相同的生命周期,并将在离开当前作用域时自动销毁。很奇怪,对吧?

块的生存时间常常需要超过它被创建的作用域。例如,你可能想要返回一个块,或保存它供稍后使用。要实现这一点,你必须拷贝这个块。你可以像处理其他 Objective-C 对象一样,向其发送 -copy 消息来完成拷贝。也和其他 Objective-C 对象一样,如果你没有在垃圾回收(Garbage Collection)环境下运行,那么你将拥有拷贝结果的所有权,并且必须最终通过 -release-autorelease 来释放它。

接下来,这是一个从方法返回块的例子:

- (void (^)(void))block { return [[^{ ... } copy] autorelease]; }

注意外部变量捕获默认提供这些变量的 const 副本。换句话说,以下写法是不合法的:

int i;
^{ i++; };
__block int i;
^{ i++; };

我将在 10.5 系统上使用 PLBlocks 展示一系列关于如何使用块的示例。想要跟随操作的读者,可以参考我构建的示例项目,你可以从这里获取我的公共 subversion 仓库:

svn co http://www.mikeash.com/svn/PLBlocksPlayground/

自定义 API
这就是它们的基本工作原理,现在让我们看看能用它们做些什么。

正如我在第一篇关于 blocks 的文章中提到的,blocks 本质上允许你构建新的控制构造而无需修改语言本身。在我们深入之前,我想先介绍一个简单的 typedef 来简化说明。大多数控制构造的 blocks 是不接受参数也不返回值的块。因此,将这种类型封装成更简洁的形式会更便于编写:

typedef void (^BasicBlock)(void);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
...
[pool release];
void WithAutoreleasePool(BasicBlock block)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
block();
[pool release];
}
for(id obj in array)
WithAutoreleasePool(^{
[self createLotsOfTemporaryObjectsWith:obj];
});

接下来我们处理一个稍微复杂些的示例。在 Cocoa 程序中,经常需要在一段短暂延迟后运行某些代码,通常使用 -performSelector:withObject:afterDelay: 方法。我们常将零延迟的含义设定为 “在返回 runloop 之后立即执行此代码”。这样做的麻烦在于,它要求一个单独的对象和一个独立的方法,而传递相关上下文可能会相当麻烦。让我们来编写一个简单的块(blocks)函数作为替代:

void RunAfterDelay(NSTimeInterval delay, BasicBlock block)
{
[[[block copy] autorelease] performSelector: @selector(my_callBlock) withObject: nil afterDelay: delay];
}
@implementation NSObject (BlocksAdditions)
- (void)my_callBlock
{
void (^block)(void) = (id)self;
block();
}
@end
NSString *something = ...;
RunAfterDelay(0, ^{
NSLog(@"%@", something);
[self doWorkWithSomething: something];
});

我们经常编写的另一类代码是受锁保护的临界区(critical section)。通常情况下,其写法如下:

[lock lock];
...do stuff...
[lock unlock];
[lock lock];
@try
{
...do stuff...
}
@finally
{
[lock unlock];
}
@implementation NSLock (BlocksAdditions)
- (void)whileLocked: (BasicBlock)block
{
[self lock];
@try
{
block();
}
@finally
{
[self unlock];
}
}
@end

关于代码风格的一处说明 上述代码中有两个有趣的选择,两者出于相同的原因。第一个选择是使用函数而非方法(method)。由于 block 是 NSObject,因此可以为它们使用 NSObject 的类别方法(category method)。我们本可以编写一个 -runAfterDelay: 的 NSObject 方法,而不是一个 RunAfterDelay 函数。第二个选择是总是将 block 参数放在最后,尽管它是最重要的参数,将其放在前面会更有意义。

做出这两个选择的原因都是,你希望 block 绝对处于最后,这样当 block 被拆分成多行时,你的代码仍然可读。例如,想象一下,将上面的函数换成方法来编写一段嵌套 block 的代码会是什么样子:

[^{
for(id obj in array)
[^{
[self doImportantWork:obj];
} withAutoreleasePool];
} runAfterDelay: 0];

在集合(Collections)中使用块(blocks)能够构建出强大的循环结构。让我们先从一个作为 NSArray 方法的、替代 for 循环的极简实现开始:

- (void)do: (void (^)(id obj))block
{
for(id obj in self)
block(obj);
}
NSArray *array = ...;
[array do: ^(id obj){ NSLog(@"%@", obj); }];
- (NSArray *)map: (id (^)(id obj))block
{
NSMutableArray *new = [NSMutableArray array];
for(id obj in self)
{
id newObj = block(obj);
[new addObject: newObj ? newObj : [NSNull null]];
}
return new;
}
NSArray *people = ...;
NSArray *names = [people map: ^(id person){
return [NSString stringWithFormat: @"%@ %@", [person firstName], [person lastName]];
}];

再举一个例子,这个允许过滤数组:

- (NSArray *)select: (BOOL (^)(id obj))block
{
NSMutableArray *new = [NSMutableArray array];
for(id obj in self)
if(block(obj))
[new addObject: obj];
return new;
}
NSArray *longStrings = [strings select: ^ BOOL (id obj) { return [obj length] > 5; }];

这是一个在 GUI 应用中使用的示例,用于获取某个特定视图内的所有文本字段(text fields):

NSArray *textFields = [[view subviews] select: ^(id obj){ return [obj isKindOfClass: [NSTextField class]]; }];

Callbacks(回调)

基于回调(Callbacks)的 API 是 blocks 大放异彩的地方。与其传递一个 selector(选择子)/delegate(代理)对,或者一个 function pointer(函数指针)/context pointer(上下文指针)对,不如传递一个 block。这使得传递上下文变得容易得多(因为 block 会自动打包所有需要的上下文),并且能让所有代码保持在附近。

回调的一个明显例子是通知(notifications)。虽然为通知使用分离的方法通常有好处,但其实现简单,有时也能让代码更优雅:

@implementation NSNotificationCenter (BlocksAdditions)
- (void)addObserverForName: (NSString *)name object: (id)object block: (void (^)(NSNotification *note))block
{
[self addObserver: [block copy] selector: @selector(my_callBlockWithObject:) name: name object: object];
}
@end

你可以这样使用它:

[[NSNotificationCenter defaultCenter] addObserverForName: NSApplicationDidBecomeActiveNotification
object: nil
block: ^(NSNotification *note){ NSLog(@"Did become active"); }];

Sheet 是 Cocoa 中一个基于回调的、令人痛苦的 API 的典型示例。你必须实现一个回调方法,然后将所有与 Sheet 相关的状态(通常很庞大)塞进提供的单个void *上下文参数中,或者塞进实例变量里。这两种方式都不太理想。

以下是一个小小的类别(category),它将这个 API 转换为使用 Block(代码块)的形式:

@implementation NSApplication (SheetAdditions)
- (void)beginSheet: (NSWindow *)sheet modalForWindow:(NSWindow *)docWindow didEndBlock: (void (^)(NSInteger returnCode))block
{
[self beginSheet: sheet
modalForWindow: docWindow
modalDelegate: self
didEndSelector: @selector(my_blockSheetDidEnd:returnCode:contextInfo:)
contextInfo: [block copy]];
}
- (void)my_blockSheetDidEnd: (NSWindow *)sheet returnCode: (NSInteger)returnCode contextInfo: (void *)contextInfo
{
void (^block)(NSInteger returnCode) = contextInfo;
block(returnCode);
[block release];
}
@end

另一个同样令人痛苦的 API 是 NSURLConnection。它提供两种模式:同步模式和异步模式。同步模式只能在 ** 后台线程(secondary thread)** 中使用,该线程可能会被阻塞任意长时间,因为任何网络操作都可能耗时良久。异步模式则需要编写大量样板代码(boilerplate code)。让我们编写一个方法,添加一种异步模式:操作完成后只需调用一个 block(代码块),将数据、响应元数据(response metadata)以及可能的错误一并传入。为此,我们只需在后台线程中使用同步 API。这段代码使用了两个函数 RunInBackgroundRunOnThread,它们是基于 block 的 API,分别用于创建新线程和在已有线程上运行 block。这些函数的实现相当直接,此处不再赘述,但如需使用,可以在示例项目中获取它们。

代码看起来是这样的:

@implementation NSURLConnection (BlocksAdditions)
+ (void)sendAsynchronousRequest: (NSURLRequest *)request
completionBlock: (void (^)(NSData *data, NSURLResponse *response, NSError *error))block
{
NSThread *originalThread = [NSThread currentThread];
RunInBackground(^{
WithAutoreleasePool(^{
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [self sendSynchronousRequest: request returningResponse: &response error: &error;];
RunOnThread(originalThread, NO, ^{ block(data, response, error); });
});
});
}
@end

以下是使用此 API 的一个示例:

NSURLRequest *request = [NSURLRequest requestWithURL: [NSURL URLWithString: @"http://www.google.com/"]];
[NSURLConnection sendAsynchronousRequest: request
completionBlock: ^(NSData *data, NSURLResponse *response, NSError *error){
NSLog(@"data: %ld bytes response: %@ error: %@", (long)[data length], response, error);
}];

注意事项 在使用 blocks 时需要注意一些事项,这并不令人意外。

当 blocks(块)被复制时,它们引用的任何局部对象变量都会被自动持有。这些变量随后会在 block 被销毁时自动释放。这对于确保引用保持有效来说很方便。任何对 self 的引用都是对一个局部对象变量的引用,这会导致 self 被持有。任何对实例变量的引用都是对 self 的隐式引用,也会导致同样的情况。然而,这在某些情况下很容易导致 retain cycle(循环引用)。想象一下,使用一个更完善的、基于 blocks 的通知 API,它允许取消注册通知。如果你的 block 以任何方式引用了 self,而你又按照标准的 Cocoa 做法在 -dealloc 方法中取消注册该通知,你的对象将会泄漏,因为该 block 将持有对你对象的引用。

一个简单的解决方法在于,__block 变量不会被自动保留(retain)。这是因为这类变量是可变的,若对其应用自动内存管理,就需要在每次变异操作时在后台生成内存管理代码。当时认为这种做法侵入性太强且难以正确实现,尤其是同一个代码块(block)可能会被多个线程同时执行。因此你可以像这样来避免保留环(retain cycle):

__block MyClass *blockSelf = self;
^{
[blockSelf message];
[blockSelf->ivar message];
};

关于块的另一个陷阱源于它们是栈对象这一事实。在底层实现中,使用 ^{...} 语法本质上等同于声明一个局部变量然后取其地址。这个地址可以被传递,但是一旦你离开该局部变量的作用域,它就不再有效。因此,像下面这样看似无害的代码,实际上是有问题的:

BasicBlock block;
if(condition)
block = ^{...};
else
block = ^{...};
BasicBlock block;
if(condition)
block = [[^{...} copy] autorelease];
else
block = [[^{...} copy] autorelease];

结论

以上就是本周的 Friday Q & A。你已经了解了如何利用当前的工具链使 blocks(代码块)运行起来,见识了一些它们如何以实用方式被运用的范例,以及需要注意的一些问题。现在你已经准备好在 10.5 应用中立即开始使用 blocks 了,无需等待 10.6 的发布。

对 blocks 有疑问?有关于如何最佳使用它们的独到见解?请在下方留言。

欢迎下周再次收看 Friday Q & A。一如既往,Friday Q & A 由你们的想法驱动。如果你有想要在此讨论的主题,请在下方留言或发送邮件给我。


#Original (English)

Source: https://www.mikeash.com/pyblog/friday-qa-2009-08-14-practical-blocks.html

Welcome back to another edition of Friday Q&A. I’m back from my break and ready to bring you more programming goodies. This week I want to take Landon Fuller’s suggestion to write a followup to my original Friday Q&A on blocks now that the design is finalized and code available for them.

Although Apple has yet to ship blocks with any of their developer tools, they have released code for their blocks implementation due to their participation in the open-source compilers gcc and clang. Landon has used this code to put together PLBlocks, which allows building and running blocks-based code on Mac OS X 10.5. While there are no blocks-based APIs on 10.5 (aside from the very basics which are part of the runtime), they can still be used to great effect.

I’m not going to cover the installation or basic use of PLBlocks here, as the PLBlocks page covers this in great detail. If you want to follow along, go there and follow the directions. For more information about how blocks work, see Clang’s blocks language spec and implementation spec.

I’m also going to assume that you know the basics of block syntax and usage, as covered in my last Friday Q&A on the subject. Today’s is essentially Part II of that one. If you haven’t already, go read that one first.

Fundamentals Blocks are Objective-C objects. When you write a block in code, that is an expression of object type, much like the @”…” constant string syntax gives you an expression of object type. You can then use this object like you would any other Objective-C object, by sending it messages that it responds to, putting it into containers, passing it as a parameter, returning it, etc.

There is a major difference from the constant string syntax. Unlike constant strings, blocks are not exactly the same each time through a piece of code. This is because blocks capture their enclosing scope, and that scope is different every time they’re called. In short, each time code execution hits a ^{…} construct, a new object is created.

Allocating a new object every time would be kind of slow, so blocks take an unusual approach: the object you get from a ^{…} construct is a stack object. This means that it has the same lifetime as local variables, and will be destroyed automatically upon leaving the current scope. Weird, huh?

It’s frequently useful for a block to outlive the scope where it was created. For example, you may want to return a block, or save it for later. For this to work, you must copy the block. You can do this like any other Objective-C object by sending it the -copy message. And like any other Objective-C object, if you aren’t running under Garbage Collection then you own the resulting object and must eventually dispose of it using -release or -autorelease.

This, then, is an example of returning a block from a method:

- (void (^)(void))block { return [[^{ ... } copy] autorelease]; }

Note that external variable capture gives const copies of those variables by default. In other words, this is not legal:

int i;
^{ i++; };
__block int i;
^{ i++; };

Examples I’m going to be showing a bunch of examples for how to use blocks with PLBlocks on 10.5. Those of you who want to follow along may wish to look at the example project I built, which you can get out of my public subversion repository here:

svn co http://www.mikeash.com/svn/PLBlocksPlayground/

Custom APIs That’s the basic idea of how they work, now let’s see what we can do with them.

As I mentioned in my first blocks post, blocks essentially allow you to build new control constructs without needing to modify the language. Before we get into it, I want to introduce a little typedef to keep things simple. Most control-construct blocks are blocks which take no parameters and return no value. As such, it’s nice to have that type wrapped up in something a little nicer to write:

typedef void (^BasicBlock)(void);
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
...
[pool release];
void WithAutoreleasePool(BasicBlock block)
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
block();
[pool release];
}
for(id obj in array)
WithAutoreleasePool(^{
[self createLotsOfTemporaryObjectsWith:obj];
});

Let’s attack something a little more complicated. It’s common in Cocoa programs to need to run some code after a short delay, using -performSelector:withObject:afterDelay:. Often we’ll use a zero delay to mean “run this code immediately after returning to the runloop”. The trouble with this is that it requires an object and a separate method, and getting the relevant context over can be painful. Let’s write a quickie blocks function instead:

void RunAfterDelay(NSTimeInterval delay, BasicBlock block)
{
[[[block copy] autorelease] performSelector: @selector(my_callBlock) withObject: nil afterDelay: delay];
}
@implementation NSObject (BlocksAdditions)
- (void)my_callBlock
{
void (^block)(void) = (id)self;
block();
}
@end
NSString *something = ...;
RunAfterDelay(0, ^{
NSLog(@"%@", something);
[self doWorkWithSomething: something];
});

Another thing that we frequently write is a critical section of code protected by a lock. Normally this would look like:

[lock lock];
...do stuff...
[lock unlock];
[lock lock];
@try
{
...do stuff...
}
@finally
{
[lock unlock];
}
@implementation NSLock (BlocksAdditions)
- (void)whileLocked: (BasicBlock)block
{
[self lock];
@try
{
block();
}
@finally
{
[self unlock];
}
}
@end

A Stylistic Note There are two interesting choices in the above code, both due to the same reason. The first choice is that these are functions, not methods. Since blocks are NSObjects, category methods on NSObjects can be used for them. Rather than a RunAfterDelay function, we could write a -runAfterDelay: method on NSObject. The second choice is to always put the block parameter last, even though it’s the most significant parameter and would make more sense to go first.

The reason for both of these is that you want the block to come absolutely last so that your code remains readable when the block is split onto multiple lines. For example, imagine some nested blocks code using the above functions recast using methods instead:

[^{
for(id obj in array)
[^{
[self doImportantWork:obj];
} withAutoreleasePool];
} runAfterDelay: 0];

Collections Using blocks with collections can make for powerful looping constructs. Let’s start out with this really simple substitute for a for loop, as a method on NSArray:

- (void)do: (void (^)(id obj))block
{
for(id obj in self)
block(obj);
}
NSArray *array = ...;
[array do: ^(id obj){ NSLog(@"%@", obj); }];
- (NSArray *)map: (id (^)(id obj))block
{
NSMutableArray *new = [NSMutableArray array];
for(id obj in self)
{
id newObj = block(obj);
[new addObject: newObj ? newObj : [NSNull null]];
}
return new;
}
NSArray *people = ...;
NSArray *names = [people map: ^(id person){
return [NSString stringWithFormat: @"%@ %@", [person firstName], [person lastName]];
}];

One more example, this allows filtering an array:

- (NSArray *)select: (BOOL (^)(id obj))block
{
NSMutableArray *new = [NSMutableArray array];
for(id obj in self)
if(block(obj))
[new addObject: obj];
return new;
}
NSArray *longStrings = [strings select: ^ BOOL (id obj) { return [obj length] > 5; }];

Here’s an example of a use in a GUI application, for getting all the text fields inside a particular view:

NSArray *textFields = [[view subviews] select: ^(id obj){ return [obj isKindOfClass: [NSTextField class]]; }];

Callbacks Callbacks-based APIs are a place where blocks really shine. Instead of passing a selector/delegate pair, or a function pointer/context pointer pair, pass a block. It makes it much easier to pass context around (since the block packages up all needed context automatically) and keeps all of the code nearby.

An obvious example of a callback is notifications. While there’s often a benefit to having separated methods for notifications, the implementation is easy and it can sometimes make for nicer code:

@implementation NSNotificationCenter (BlocksAdditions)
- (void)addObserverForName: (NSString *)name object: (id)object block: (void (^)(NSNotification *note))block
{
[self addObserver: [block copy] selector: @selector(my_callBlockWithObject:) name: name object: object];
}
@end

You can use it like so:

[[NSNotificationCenter defaultCenter] addObserverForName: NSApplicationDidBecomeActiveNotification
object: nil
block: ^(NSNotification *note){ NSLog(@"Did become active"); }];

Sheets are a good example of a painful callbacks-based API in Cocoa. You have to implement a callback method, then cram all of the state associated with the sheet, which is often large, either into the single void * context parameter provided, or into instance variables. Neither way is particularly nice.

Here is a small category which translates this API to use blocks instead:

@implementation NSApplication (SheetAdditions)
- (void)beginSheet: (NSWindow *)sheet modalForWindow:(NSWindow *)docWindow didEndBlock: (void (^)(NSInteger returnCode))block
{
[self beginSheet: sheet
modalForWindow: docWindow
modalDelegate: self
didEndSelector: @selector(my_blockSheetDidEnd:returnCode:contextInfo:)
contextInfo: [block copy]];
}
- (void)my_blockSheetDidEnd: (NSWindow *)sheet returnCode: (NSInteger)returnCode contextInfo: (void *)contextInfo
{
void (^block)(NSInteger returnCode) = contextInfo;
block(returnCode);
[block release];
}
@end

Another example of a similarly painful API is NSURLConnection. It provides two modes: synchronous and asynchronous. The synchronous mode can only be used from a secondary thread which can be blocked for arbitrarily long amounts of time, since any network operation can take a long time to complete. The asynchronous mode requires writing a lot of boilerplate code. Let’s write a method that adds an asynchronous mode that simply makes a single call to a block when it’s done to hand over the data, the response metadata, and the error, if any. To do this, we’ll just use the synchronous API in a background thread. This code uses two functions, RunInBackground and RunOnThread, which are blocks-based APIs for spawning a new thread and running a block on an existing thread, respectively. These functions are pretty straightforward to implement and I won’t duplicate them here, but you can get them in the sample project if you need.

Here’s what the code looks like:

@implementation NSURLConnection (BlocksAdditions)
+ (void)sendAsynchronousRequest: (NSURLRequest *)request
completionBlock: (void (^)(NSData *data, NSURLResponse *response, NSError *error))block
{
NSThread *originalThread = [NSThread currentThread];
RunInBackground(^{
WithAutoreleasePool(^{
NSURLResponse *response = nil;
NSError *error = nil;
NSData *data = [self sendSynchronousRequest: request returningResponse: &response error: &error;];
RunOnThread(originalThread, NO, ^{ block(data, response, error); });
});
});
}
@end

Here’s an example of using this API:

NSURLRequest *request = [NSURLRequest requestWithURL: [NSURL URLWithString: @"http://www.google.com/"]];
[NSURLConnection sendAsynchronousRequest: request
completionBlock: ^(NSData *data, NSURLResponse *response, NSError *error){
NSLog(@"data: %ld bytes response: %@ error: %@", (long)[data length], response, error);
}];

Caveats It should be no surprise that there are some things to look out for when working with blocks.

When blocks are copied, any local object variables they refer to get automatically retained. They are then automatically released when the block is destroyed. This is convenient to ensure that the references remain valid. Any reference to self is a reference to a local object variable, causing self to be retained. Any reference to an instance variable is an implicit reference to self and causes the same thing. However, this makes it easy to cause a retain cycle in some instances. Imagine using a more fleshed-out version of that blocks-based notification API which allows unregistering the notification. If your block refers to self in any way, and you do the standard Cocoa thing of unregistering the notification in -dealloc, your object will leak because the block will hold a reference to your object.

A simple workaround to this lies in the fact that __block variables are not retained. This is because such variables are mutable, and automatic memory management of them would require each mutation to generate memory management code behind the scenes. This was seen as too intrusive and difficult to get right, especially since the same block may be executing from multiple threads simultaneously. Thus you can avoid the retain cycle like so:

__block MyClass *blockSelf = self;
^{
[blockSelf message];
[blockSelf->ivar message];
};

Another pitfall with blocks is due to the fact that they are stack objects. Using the ^{…} syntax is essentially the same, behind the scenes, as declaring a local variable and then taking its address for something. The address can be passed around, but as soon as you leave the scope in which the local variable was declared, it’s no longer valid. Thus, something as innocent as this ends up being broken code:

BasicBlock block;
if(condition)
block = ^{...};
else
block = ^{...};
BasicBlock block;
if(condition)
block = [[^{...} copy] autorelease];
else
block = [[^{...} copy] autorelease];

Conclusion That wraps up this week’s Friday Q&A. You’ve seen how to get blocks up and running with your current tool chain, a few examples of how they can be used in a useful manner, and some problems to watch out for. Now you’re ready to start using blocks in your 10.5 apps today, no need to wait for 10.6 to ship.

Questions about blocks? Have your own ideas for how best to use them? Post below.

Come back next week for another edition of Friday Q&A. As always, Friday Q&A is driven by your ideas. If you have a topic that you would like to see covered here, post it below or e-mail it to me.