iOS开发之block详解
来源:互联网 发布:centos 7开机密码忘记 编辑:程序博客网 时间:2024/05/16 13:43
前言
今天研究了一下iOS开发中的block,有些心得,故写下来。默认读者会是有 iOS开发经验的,故相关内容不再科普。
1.block入门
老子说:学习编程,从翻译开始。block的翻译是什么?是代码块。代码块是个什么鬼?
block就是一块可以使用的代码,在定义和声明上和变量类似,在使用上和函数类似。
下面上demo,首先是全局的代码块:
void (^foo)(void) = ^(void){ NSLog(@"Hello World!");};
以及全局变量:
static int _index = 0;
和函数
void func(void) { NSLog(@"Hello World!");}
由上面可以看到,block即像函数一样有参数列表,又像基本数据类型一样是有定义声明,最后需要接分号“;”的。
而block在使用中,更加像函数:
foo();
而拆开的block的定义和声明,又和变量的定义和声明比较像:
void (^block)(void);block = ^(void){ NSLog(@"Hello World!");};
对block有了基本的认识,下面来看下block有哪些特性,以及需要注意的问题。
2.block访问局部变量
block对上下文局部变量的访问,又有一个高大上的名词,就是“闭包性”。事实上,block的上下文访问,并不是直接访问,而是做了一个备份,对备份的访问。
2.1.block访问局部变量——读取
block对内部局部变量的控制,类似于一个维护着的参数表。block内部使用到的所有变量,都会另外申请新的内存空间。对于block的参数,自然是和函数的参数类似,遵循一般的取值取址操作。对于block访问的外部的局部变量,是获取的一份局部变量的数据拷贝。
所以,局部变量的声明周期结束,所占的内存释放后,block仍旧可以获取其中的数据:
void (^block)(void);- (void)blockTest01 { int i = 10; block = ^(void) { NSLog(@"%d",i); };}- (void)blockTest11 { [self blockTest1]; block();}
通过打印地址,我们可以看到,block对局部变量的访问确实是值访问。
- (void)blockTest02 { int i = 10; NSLog(@"局部变量i --- %p", &i); block = ^(void) { NSLog(@"block访问i --- %p",&i); };}
运行结果为:
局部变量i --- 0x7fff58ef2a7cblock访问i --- 0x7fd8fa507280
以上,基本可以确定,block访问局部变量是单独拷贝了一份数据。说[访问][6]其实不是很准确,因为局部变量的声明周期早已结束,这里实际上是block私下备了个份。
而block备份的方式是值拷贝,所以block对局部变量的访问会出现和函数参数类似的值传递、址传递的问题。具体下面说。
2.2.block访问局部变量——址传递的修改
有C语言功底的朋友应该知道,指针中存放的值,是实际值在内存中的地址。而block中对局部变量的访问,就是一种值访问。
当该局部变量是一般的数据时:
int i = 0;
拷贝的是i的值,即:“0”。因此,block对于该局部变量的修改,仅限于其备份,而对其本身是起不到任何作用的。
然而,当局部变量是指针时:
char *str = "Hello";
str中存储的是“Hello”这个字符串在内存中所在的位置。即便局部变量的声明周期结束后,由于block中的str的备份仍然持有该内存,故该内存是不会被释放,此处遵守C语言内存管理原则[谁拥有谁释放][6]。由于block持有了str的地址,因此对其备份的修改,就是对内存地址所指向内容的修改。
当然,上例中,str是在常量区的,是无法直接修改的,这又是另一个话题了。
至此,block对一般的局部变量的访问就讲完了。
2.3.block访问局部变量——值传递的修改
接上节,当遇到block值传递数据,比如:
int i = 0;
我们怎么实现对其的修改呢。根据我们对C语言值传递的经验,我们很容易知道,只要重新获得i的地址,进行传递就可以了,比如:
- (void)blockTest03 { static int i = 10; int *pi = &i; block = ^(void) { (*pi)++; NSLog(@"值:%d,址:%p",*pi, pi); }; NSLog(@"局部变量i:%d",i);}
当我们多次运行block,再重新打印局部变量时:
[self blockTest03]; block(); block(); block(); [self blockTest03];
会发现
局部变量i:10值:11,址:0x10205fe18值:12,址:0x10205fe18值:13,址:0x10205fe18局部变量i:13
也就是说,我们成功的通过址传递,完成了在block内部对局部变量的修改。
当然,这种方法很C风格,对OC程序员很不友好,因此苹果给了我们一个代码糖__block,来实现相同的功能。
__block typeof(i) bi = i;
这里的bi在使用中和*pi相同。
总结一下:
__block是用来将局部变量的访问改为取址操作的。
简单的讲,就是解决无法修改局部变量值的一个方法。
2.4.block访问局部变量——循环引用
当出现以下场景时:某个类FooClass有一个成员变量barBlock,以及一个成员变量name。
当barBlock内部通过self使用了name时,就会出现以下情况:barBlock持有self的地址,self持有barBlock的地址,二者内存地址相互持有,导致二者均无法正确释放,造成内存泄露。
用OC的说法就是,二者相互强引用,导致内存无法释放。解决办法就是,将其中一个设置为若引用。
这个就是我们常说的block循环引用,以及其解决办法:
__weak typeof(self) wSelf = self;
总结一下:
__weak是用来解决在block调用“拥有该block的对象的成员变量”时,造成的循环引用问题的。
简单的将,就是当block里面用到self时,要用上面的代码。
2.5.__block和__weak的区别
如果读过上面的部分,应该知道,二者的区别是很大的。只是由于二者用的时候看起来很像,故而有可能会被混淆。
总结一下:
__block是对于无法修改的局部变量,使得block能够修改的方法
__weak是对于可修改的变量self,解决其使用中会造成循环引用问题的一个办法。
3.block的本质
其实从底层实现上,block和NSObject一样,是用struct实现的。这也就解释了,为什么block的定义和声明会和变量的声明非常相似。
block的实现以及其闭包性,就是通过该struct实现的。该struct内有两个比较重要的成员:指向实际执行的函数指针、block内部调用的成员变量表。就是在该变量表中,存放着上下文临时变量的拷贝。
具体的细节,有一篇转载的博文Block的引用循环问题 (ARC & non-ARC),以及Objective-C中的Block),讲的比较详细,有兴趣的同学可以去研究一下。
- iOS开发之block详解
- IOS开发之Block详解
- iOS开发之Block详解
- iOS 之Block详解
- IOS之Block详解
- iOS开发之Block
- 【ios开发系列】block详解
- iOS开发-block详解
- iOS 开发 block深入浅出详解
- iOS开发之---block的使用(详解)
- IOS开发之Block编程
- ios开发之Block语法
- IOS开发之block应用
- IOS开发之Block编程
- IOS开发之Block语法
- IOS开发之Block语法
- iOS开发之BLOCK应用
- iOS开发之Block日记
- The 5th Zhejiang Provincial Collegiate Programming Contest---ProblemG:Give Me the Number
- 【千里之行,始于足下】游戏服务端开发--开发语言篇
- Shader 学习一:Shader和渲染管线
- 基于ffmpeg的HLS开源服务器搭建配置及开发详解
- 学C++第一天 面向对象写2杈
- iOS开发之block详解
- The 5th Zhejiang Provincial Collegiate Programming Contest------ProblemK:Kinds of Fuwas
- Shader 学习二:Unity shader 的组织形式
- Basic C++ Container classes summary(Array, Vector, List)
- 【IntentService】原理解析
- 解决window平台下JNI生成头文件找不到"xxx"类文件
- The 6th Zhejiang Provincial Collegiate Programming Contest->ProblemA:Second-price Auction
- 固定管线shader编写:基本属性
- The 6th Zhejiang Provincial Collegiate Programming Contest->ProblemB:Light Bulb