关于block的一点研究

关于block的使用,网上资料已经足够多了,本文不再赘述。

以及本人对arc没有太大好感,毕竟太智能了。这篇文章基于手动内存管理写成。

block与CocoaObj的区别

从编译器来说,block最后被编译成了一个c struct。如下图

这决定了它是在编译时就分配好了空间,于是就引入了一个问题:它是全部被放在栈(stack)空间内的。而cocoaobj全都是指针,它的指针是存在于栈空间内的,但是数据是放在堆(heap)空间内的。

那么,栈空间和堆空间有什么区别呢?

这里不说效率上的问题,我们不是在打竞赛。

问题出在于栈控件内的数据,只存在于当前区域内。一旦出了它的有效区域例如退出函数,离开if分支,离开循环。他就会被释放掉。哪怕你保留了指针,他也不再是这个东西了。毕竟被pop掉了。

block使用细节

为什么要一再强调block是栈空间数据,是因为在使用的时候会遇到一些奇怪的问题。

例如我有一堆任务(动画)想让他按顺序执行,我可以把它都写成block,然后塞入数组。在想需要用的时候直接调用。

写了一个demo

1
2
3
4
5
typedef int (^IntBlock)(void);
IntBlock Block_001 = ^{ return 101; };
IntBlock Block_002 = ^{ return 202; };
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];
int x = ((IntBlock)[array objectAtIndex:0]) ();

嗯,是不是很不错?

但是遇到了一个很大的bug。如果这段代码写在了一个function里面,但调用它的时候已经出了function,就会产生崩溃,因为array里面就成了野指针了。

毕竟NSArray只会对里面的数据做一次 retain而已。

这个时候,就需要使用copy。将栈空间的block拷贝到堆空间内。 改为了这样:

1
2
3
int (^Block_001)(void) = [^{ return 101; } copy];
int (^Block_002)(void) = [^{ return 202; } copy];
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];

写法很怪是不是,但是很有效。不过还有问题。

从objective-c 内存管理角度来看,你copy了一次,就相当于对他做了一个retain。他的retainCnt就等于1了

然后添加到了数组中,retainCnt就等于2。

于是又导致了内存泄露。

于是最终写法应该是这样:

1
2
3
4
5
6
int (^Block_001)(void) = [^{ return 101; } copy];
int (^Block_002)(void) = [^{ return 202; } copy];
NSArray *array = [NSArray arrayWithObjects:Block_001, Block_002, nil];

[Block_001 release];
[Block_002 release];

本文中的demo引用于:

http://stackoverflow.com/questions/3257940/executing-blocks-from-nsarray

Comments