Objective-C基于C语言闭包Block的实现
2013-11-11 17:20
405 查看
0.简单的Block定义和中间实现代码
先写一个简单的Block程序block.c:#include <stdio.h> typedef void(^TestBlock)(void); int main() { TestBlock testBlock = ^(){ printf("Hello World!\n"); }; testBlock(); return 0; }在命令行下用Clang工具的-rewrite-objc编译选项编译:
clang -rewrite-objc block.c得到一份block.cpp的C语言中间代码,其中关于Block的主要代码如下:
struct __block_impl { void *isa; int Flags; int Reserved; void *FuncPtr; }; typedef void(*TestBlock)(void); struct __main_block_impl_0 { struct __block_impl impl; struct __main_block_desc_0* Desc; __main_block_impl_0(void *fp, struct __main_block_desc_0 *desc, int flags=0) { impl.isa = &_NSConcreteStackBlock; impl.Flags = flags; impl.FuncPtr = fp; Desc = desc; } }; static void __main_block_func_0(struct __main_block_impl_0 *__cself) { printf("Hello World!\n"); } static struct __main_block_desc_0 { size_t reserved; size_t Block_size; } __main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)}; int main() { TestBlock testBlock = (void (*)())&__main_block_impl_0((void *)__main_block_func_0, &__main_block_desc_0_DATA); ((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock); return 0; }
1.函数调用的理解
对于这个简单的Block实现别人也讲过很多遍了,我只有一处看明白的比较慢就是下面这行函数调用的代码:((void (*)(__block_impl *))((__block_impl *)testBlock)->FuncPtr)((__block_impl *)testBlock);
testBlock是void(*)(void)类型的函数指针,指向了一个__main_block_impl_0结构体,由于__main_block_impl_0结构体的内存第一部分是__block_impl,就可以通过指针的强制类型转换((__block_impl *)testBlock)->FuncPtr得到函数指针,由于__block_impl的函数指针类型是void *类型的此时就可以同样通过类型转换得到(void (*)(__block_impl
*))类型的函数指针,虽然此时函数指针和真正调用的函数类型(void (*)(struct __main_block_impl_0 *__cself))不同,但是实际提供的参数testBlock实际上是__main_block_impl_0类型的。这里的关键就是__main_block_impl_0结构体内存结构以__block_impl结构体开始,这就像父子类关系一样,__block_impl扮演父类角色,__main_block_impl_0扮演子类角色。通过指针类型的强制转换来实现通用的Block函数调用。说是通用就是无论实现多少个Block都可以通过同样的这样的形式调用Block真正的执行函数。
2.Block类型的理解
(1)在非ARC下,这里虽然isa指向的是NSConcreteStackBlock,但是在LLVM编译下没有访问局部变量的Block应该是NSConcreteGlobalBlock类型的,访问了局部变量的Block是NSConcreteStackBlock类型的。(2)在ARC下,访问了局部变量的Block是NSConcreteMallocBlock类型的,未访问局部变量的Block是NSConcreteGlobalBlock类型的。
3.Block对变量访问的理解
(1)普通栈变量 :通过传值实现一次性可读访问权限。(2)static变量 :通过传地址实现读写权限。
(3)全局变量 :本身就具有可读写权限。
(4)__block变量 :通过把普通变量打包成__Block_byref_*_0结构体来传递地址实现变量的读写权限。__Block_byref_a_0结构体如下:
struct __Block_byref_a_0 { void *__isa; __Block_byref_a_0 *__forwarding; int __flags; int __size; int a; };其中__forwarding在初始化的时候是指向自身的,当NSConcreteStackBlock类型的Block通过copy变为NSConcreteMallocBlock时,栈空间的Block的__forwarding指针会改变指向堆空间的Block。
4.Block中循环引用的解决
使用Block容易产生Retain Cycle。这是因为Block会自动retain他引用的对象。在使用SDWebImage时一个简单的Retain Cycle例子:UIImageView *imgView = self.imageView; [self.imageView setImageWithURL:url completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) { // ... imgView.image = image; // warning: Capturing 'imgView' strongly in this block is likely to lead to a retain cycle }];self.imageView在调用setImageWithURL:completed:时会retain定义的这个Block,而Block用到了imgView又会自动retainimgView,所以就造成了Retain Cycle。解决办法:
(1)非ARC下
__block UIImageView *weakImgView = self.imageView; [self.imageView setImageWithURL:url completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType) { // ... weakImgView.image = image; }];
添加:在不用__block的说明符的情况下,当Block发生copy的时候会retain Block内部用到的对象实例。在用了__block说明符的情况下,打包成的
struct __Block_byref_a_0
只是记录的用到的对象的指针,在copy的时候并没有retain结构体内部的对象实例。
(2)ARC下
ARC下也是可以用__block打破循环引用的:
__block UIImageView *weakImgView = self.imageView;
[self.imageView setImageWithURL:url completed:^(UIImage *image, NSError *error, SDImageCacheType cacheType)
{
// ...
weakImgView.image = image;
//注意下面这句
weakImgView = nil;
}];
__block变量打包成的结构体内部对对象变量的引用是强引用,同样会产生循环引用,只不过是我们在Block执行最后的时候打破了(注释下面那句),但是如果我们不执行这个Block,循环引用还是存在的。所以最好用下面的方法。
__weak UIImageView *weakImgView = self.imageView; [self.imageView setImageWithURL:url completed:^(UIImage *image,NSError *erroe,SDImageCacheType cacheType) { // ... weakImgView.image = image; }
为了异步操作回调的时候防止weakImgView为nil,我们可以这么做:
__weak UIImageView *weakImgView = self.imageView; [self.imageView setImageWithURL:url completed:^(UIImage *image,NSError *erroe,SDImageCacheType cacheType) { UIImageView *strongImgView = weakImgView; // ... strongImgView.image = image; }
另外注:
(1)NSNotificationCenter的addObserverForName:object:queue:usingBlock:会retainBlock。
(2)NSTimer会retain目标Target。
(3)如果在iOS5.0之前,__unsafe_unretained 替代 __block
5.Block的使用
http://blog.csdn.net/joywii/article/details/99030956.参考链接
LLVM开源的Block实现源码:https://llvm.org/svn/llvm-project/compiler-rt/trunk/BlocksRuntime/谈Objctive-C Block的实现:http://blog.devtang.com/blog/2013/07/28/a-look-inside-blocks/
iOS中Block实现的探究:http://blog.csdn.net/jasonblog/article/details/7756763
Objctive-C Blocks Quiz:http://blog.parse.com/2013/02/05/objective-c-blocks-quiz/
对Objective-C中Block的追探:http://www.cnblogs.com/biosli/archive/2013/05/29/iOS_Objective-C_Block.html
正确的使用Block http://tanqisen.github.io/blog/2013/04/19/gcd-block-cycle-retain/
相关文章推荐
- 基于C语言实现三子棋
- Objective-C的Block的实现原理
- 基于C语言的状态机实现技术
- 【Objective-C高级编程】iOS与OS X多线程和内存管理(四) Block的实现
- 队列(C语言实现,基于链式结构)
- iOS开发之OC与swift开发混编教程,代理的相互调用,block的实现。OC调用Swift中的代理, OC调用Swift中的Block 闭包,swift 3.0
- Rob Hess的SIFT算法的C语言实现(基于OpenCV)
- Rob Hess的SIFT算法的C语言实现(基于OpenCV)
- c语言基于Linux下用libpcap实现抓包程序
- 基于哈夫曼(haffuman)算法的文件压缩的实现(C语言)(转)
- [Objective-C]用Block实现链式编程
- Objective-C中的Block(闭包)
- 基于C语言的内存池的设计与实现
- 编译Rob Hess基于OpenCV的SIFT算法的C语言实现
- C语言实现基于最大堆和最小堆的堆排序算法示例
- 基于C语言实现的Ping程序
- Rob Hess的SIFT算法的C语言实现(基于OpenCV)(调通!!!)
- C语言基于GTK+Libvlc实现的简易视频播放器(二)
- Linux中使用C语言实现基于UDP协议的Socket通信示例
- 从C语言的变量声明到Objective-C中的Block语法转载]