如何在 block 中修改block 外部变量量

OC高级编程——深入block,如何捕获变量,如何存储在堆上
首先先看几道block相关的题目
这是一篇比较长的博文,前部分是block的测试题目,中间是block的语法、特性,block讲解block内部实现和block存储位置,请读者耐心阅读。具备block基础的同学,直接调转到block的实现
首先先看几道block相关的题目
这是一篇比较长的博文,前部分是block的测试题目,中间是block的语法、特性,block讲解block内部实现和block存储位置,请读者耐心阅读。具备block基础的同学,直接调转到block的实现
下面列出了五道题,看看能否答对两三个。主要涉及block栈上、还是堆上、怎么捕获变量。答案在博文最后一行
//-----------第一道题:--------------
void exampleA() {
char a = 'A';
^{ printf("%c\n", a);};
A.始终能够正常运行
B.只有在使用ARC的情况下才能正常运行
C.不使用ARC才能正常运行
D.永远无法正常运行
//-----------第二道题:答案同第一题--------------
void exampleB_addBlockToArray(NSMutableArray *array) {
char b = 'B';
[array addObject:^{printf("%c\n", b);}];
void exampleB() {
NSMutableArray *array = [NSMutableArray array];
exampleB_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
//-----------第三道题:答案同第一题--------------
void exampleC_addBlockToArray(NSMutableArray *array) {
[array addObject:^{printf("C\n");}];
void exampleC() {
NSMutableArray *array = [NSMutableArray array];
exampleC_addBlockToArray(array);
void (^block)() = [array objectAtIndex:0];
//-----------第四道题:答案同第一题--------------
typedef void (^dBlock)();
dBlock exampleD_getBlock() {
char d = 'D';
return ^{printf("%c\n", d);};
void exampleD() {
exampleD_getBlock()();
//-----------第五道题:答案同第一题--------------
typedef void (^eBlock)();
eBlock exampleE_getBlock() {
char e = 'E';
void (^block)() = ^{printf("%c\n", e);};
void exampleE() {
eBlock block = exampleE_getBlock();
注:以上题目摘自:CocoaChina点击打开链接
什么是block
Blocks是C语言的扩充功能。可以用一句话来表示Blocks的扩充功能:带有自动变量(局部变量)的匿名函数。命名就是工作的本质,函数名、变量名、方法名、属性名、类名和框架名都必须具备。而能够编写不带名称的函数对程序员来说相当有吸引力。
例如:我们要进行一个URL的请求。那么请求结果以何种方式通知调用者呢?通常是经过代理(delegate)但是,写delegate本身就是成本,我们需要写类、方法等等。
这时候,我们就用到了block。block提供了类似由C++和OC类生成实例或对象来保持变量值的方法。像这样使用block可以不声明C++和OC类,也没有使用静态变量、静态全局变量或全局变量,仅用编写C语言函数的量即可使用带有自动变量值的匿名函数。
其他语言中也有block概念。
block的实现
block的语法看上去好像很特别,但实际上是作为极为普通的C语言代码来处理的。这里我们借住clang编译器的能力:具有转化为我们可读源代码的能力。
控制台命令是: clang -rewrite-objc 源代码文件名。
int main(){
void (^blk)(void) = ^{printf("block\n");};
经过 clang -rewrite-objc 之后,代码这样了(简化后代码,读者可以搜索关键字在生成文件中查找):
struct __block_impl{
void *FuncP
static struct __main_block_desc_0{
unsigned long Block_size
}__main_block_desc_0_DATA = { 0, sizeof(struct __main_block_impl_0)};
struct __main_block_impl_0{
struct __block_
struct __main_block_desc_0 *D
static struct __main_block_func_0(struct __main_block_impl_0 *__cself)
printf("block\n");
int main(){
struct __main_block_impl_0 *blk =
&__main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA);
(*blk->impl.FuncPtr)(blk);
很多结构体,很多下划线的变量和函数名。我们一个个来:
__block_impl:更像一个block的基类,所有block都具备这些字段。
__main_block_impl_0:block变量。
__main_block_func_0:虽然,block叫,匿名函数。但是,这个函数还是被编译器起了个名字。
__main_block_desc_0:block的描述,注意,他有一个实例__main_block_desc_0_DATA
上述命名是有规则的:main是block所在函数的名字,后缀0则是这个函数中的第0个block。由于上面是C++的代码,可以将__main_block_impl_0的结构体总结一下,得到如下形式:
__main_block_impl_0{
void *FuncP
struct __main_block_desc_0 *D
总结:所谓block就是Objective-C的对象
截获自动变量值
int val = 10;
void (^blk)(void) = ^{printf("val=%d\n",val);};
上面这段代码,输出值是:val = 10.而不是2.block截获自动变量的瞬时值。因为block保存了自动变量的值,所以在执行block语法后,即使改写block中使用的自动变量的值也不会影响block执行时自动变量的值。
尝试改写block中捕获的自动变量,将会是编译错误。我更喜欢把这个理解为:block捕获的自动变量都将转化为const类型。不可修改了
解决办法是将自动变量添加修饰符 __block;那么如果截获的自动变量是OC对象呢
^{[array addObject:obj];};
这么写是没有问题的,因为array是一个指针,我们并没有改变指针的值。这个也可以解释下面的问题
const char text[] = "hello";
printf("%c\n",text[2]);};
这样会编译错误。为何?这是因为捕获自动变量的方法并没有实现C语言数组类型。可以通过指针代替:const char *text= "hello";
那么这个block的对象结构是什么样呢,请看下面:
__main_block_impl_0{
void *FuncP
struct __main_block_desc_0 *D
这个val是如何传递到block结构体中的呢?
int main(){
struct __main_block_impl_0 *blk =
&__main_block_impl_0(__main_block_func_0,&__main_block_desc_0_DATA,val);
}注意函数调用最后一个参数,即val参数。
那么函数调用的代码页转化为下面这样了.这里的cself跟C++的this和OC的self一样。
static struct __main_block_func_0(struct __main_block_impl_0 *__cself)
printf("val=%d\n",__cself-val);
}所以,block捕获变量更像是:函数按值传递。
__block说明符
前面讲过block所在函数中的,捕获自动变量。但是不能修改它,不然就是编译错误。但是可以改变全局变量、静态变量、全局静态变量。
其实这两个特点不难理解:第一、为何不让修改变量:这个是编译器决定的。理论上当然可以修改变量了,只不过block捕获的是自动变量的副本,名字一样。为了不给开发者迷惑,干脆不让赋值。道理有点像:函数参数,要用指针,不然传递的是副本。
第二、可以修改静态变量的值。静态变量属于类的,不是某一个变量。所以block内部不用调用cself指针。所以block可以调用。
解决block不能保存值这一问题的另外一个办法是使用__block修饰符。
__block int val = 10;
void (^blk)(void) = ^{val = 1;};
该源码转化后如下:
struct __block_byref_val_0{
__block_byref_val_0 *__
__main_block_impl_0中自然多了__block_byreg_val_0的一个字段。注意:__block_byref_val_0结构体中有自身的指针对象,难道要
_block int val = 10;这一行代码,转化成了下面的结构体
__block)byref_val_0 val = {0,&val,0,sizeof(__block_byref_val_0),10};//自己持有自己的指针。
它竟然变成了结构体了。之所以为啥要生成一个结构体,后面在详细讲讲。反正不能直接保存val的指针,因为val是栈上的,保存栈变量的指针很危险。
block存储区域
这就需要引入三个名词:
● _NSConcretStackBlock
● _NSConcretGlobalBlock
● _NSConcretMallocBlock
正如它们名字说的那样,说明了block的三种存储方式:栈、全局、堆。__main_block_impl_0结构体中的isa就是这个值。
【要点1】如果是定义在函数外面的block是global的,另外如果函数内部的block但是,没有捕获任何自动变量,那么它也是全局的。比如下面这样的代码:
typedef int (^blk_t)(int);
blk_t blk = ^(int count) {};
虽然,这个block在循环内,但是blk的地址总是不变的。说明这个block在全局段。
【要点2】一种情况在非ARC下是无法编译的:
typedef int(^blk_t)(int);
blk_t func(int rate){
return ^(int count){return rate*}
这是因为:block捕获了栈上的rate自动变量,此时rate已经变成了一个结构体,而block中拥有这个结构体的指针。即如果返回block的话就是返回局部变量的指针。而这一点恰是编译器已经断定了。在ARC下没有这个问题,是因为ARC使用了autorelease了。
【要点3】有时候我们需要调用block 的copy函数,将block拷贝到堆上。看下面的代码:
-(id) getBlockArray{
int val =10;
return [[NSArray alloc]initWithObjects:
^{NSLog(@"blk0:%d",val);},
^{NSLog(@"blk1:%d",val);},nil];
id obj = getBlockArray();
typedef void (^blk_t)(void);
blk_t blk = (blk_t){obj objectAtIndex:0};
这段代码在最后一行blk()会异常,因为数组中的block是栈上的。因为val是栈上的。解决办法就是调用copy方法。
【要点4】不管block配置在何处,用copy方法复制都不会引起任何问题。在ARC环境下,如果不确定是否要copy block尽管copy即可。ARC会打扫战场。
注意:在栈上调用copy那么复制到堆上,在全局block调用copy什么也不做,在堆上调用block 引用计数增加
【注意】本人用Xcode 5.1.1 iOS sdk 7.1 编译发现:并非《Objective-C》高级编程这本书中描述的那样
int val肯定是在栈上的,我保存了val的地址,看看block调用前后是否变化。输出一致说明是栈上,不一致说明是堆上。
typedef int (^blkt1)(void) ;
-(void) stackOrHeap{
__block int val =10;
int *valPtr = &//使用int的指针,来检测block到底在栈上,还是堆上
blkt1 s= ^{
NSLog(@"val_block = %d",++val);
NSLog(@"valPointer = %d",*valPtr);
在ARC下——block捕获了自动变量,那么block就被会直接生成到堆上了。 val_block = 11 valPointer = 10
在非ARC下——block捕获了自动变量,该block还是在栈上的。 val_block = 11 valPointer = 11
调用copy之后的结果呢:
-(void) stackOrHeap{
__block int val =10;
int *valPtr = &//使用int的指针,来检测block到底在栈上,还是堆上
blkt1 s= ^{
NSLog(@"val_block = %d",++val);
blkt1 h = [s copy];
NSLog(@"valPointer = %d",*valPtr);
----------------在ARC下>>>>>>>>>>>无效果。 val_block = 11 valPointer = 10
----------------在非ARC下>>>>>>>>>确实复制到堆上了。 val_block = 11 valPointer = 10
用这个表格来表示
/*当block捕获了自动变量时候
------------------------------------------------------------------
block stay
-------------------------------------------------------------------
------------------------------------------------------------------
------------------------------------------------------------------
__block变量存储区域
当block被复制到堆上时,他所捕获的对象、变量也全部复制到堆上。
回忆一下block捕获自动变量的时候,自动变量将编程一个结构体,结构体中有一个字段叫__forwarding,用于指向自动这个结构体。那么有了这个__forwarding指针,无论是栈上的block还是被拷贝到堆上,那么都会正确的访问自动变量的值。
block会持有捕获的对象。编译器为了区分自动变量和对象,有一个类型来区分。
static void __main_block_copy_0(struct __main_block_impl_0 *dst, struct __main_block_impl_0 *src){
_Block_objct_assign(&dst->val,src->val,BLOCK_FIELD_IS_BYREF);
static void __main_block_dispose_0(struct __main_block_impl_0 *src){
_block_object_dispose(src->val,BLOCK_FIELD_IS_BYREF);
BLOCK_FIELD_IS_BYREF代表是变量。BLOCK_FIELD_IS_OBJECT代表是对象
【__block变量和对象】
__block修饰符可用于任何类型的自动变量
【__block循环引用】
根据上面讲的内容,block在持有对象的时候,对象如果持有block,会造成循环引用。解决办法有两种:
1. 使用__weak修饰符。id __weak obj = obj_
2. 使用__block修饰符。__block id tmp =然后在block中tmp = nil;这样就打破循环了。这个办法需要记得将tmp=nil。不推荐!
文章开头block测试题答案:ABABB
int val = 10;
void (^blk)(void) = ^{printf("val=%d\n",val);};
上面这段代码,输出值是:val = 10.而不是2.block截获自动变量的瞬时值。因为block保存了自动变量的值,所以在执行block语法后,即使改写block中使用的自动变量的值也不会影响block执行时自动变量的值。
尝试改写block中捕获的自动变量,将会是编译错误。我更喜欢把这个理解为:block捕获的自动变量都将转化为const类型。不可修改了
解决办法是将自动变量添加修饰符 __block;那么如果截获的自动变量是OC对象呢
^{[array addObject:obj];};
这么写是没有问题的,因为array是一个指针,我们并没有改变指针的值。这个也可以解释下面的问题
const char text[] = "hello";
printf("%c\n",text[2]);};
这样会编译错误。为何?这是因为捕获自动变量的方法并没有实现C语言数组类型。可以通过指针代替:const char *text= "hello";我们可以把Block当做Objective-C的匿名函数。Block允许开发者在两个对象之间将任意的语句当做数据进行传递,往往这要比引用定义在别处的函数直观。另外,block的实现具有封闭性(closure),而又能
&本文转自的博客:
本周末微博上朋友发了一个关于block的MV,只能说老外太逗了。大家也可以去看看怎么回事:。虽然之前也有接触过block,不过没有深入完整的学习过,借此机会来学习一下,顺便翻译几篇block相关的文章,本文是第一篇,算是block的入门。本文的最后延伸阅读给出了4篇相关文章,不出意外的话,本周大家能看到对应的中文版。
Block的创建
不带参数的Block
Block的闭包性(closure)
修改非局部变量
Block作为函数的参数
定义Block类型
我们可以把Block当做Objective-C的匿名函数。Block允许开发者在两个对象之间将任意的语句当做数据进行传递,往往这要比引用定义在别处的函数直观。另外,block的实现具有封闭性(closure),而又能够很容易获取上下文的相关状态信息。
Block的创建
实际上,block使用了与相同的机制:可以像声明函数一样,来声明一个bock变量;可以利用定义一个函数的方法来定义一个block;也可以将block当做一个函数来调用。
&#import&&Foundation/Foundation.h&&&&int&main(int&argc,&const&char&*&argv[])&{&&&&&@autoreleasepool&{&&&&&&&&&&&&&&&&&&double&(^distanceFromRateAndTime)(double&rate,&double&time);&&&&&&&&&&&&&&&&&&&&distanceFromRateAndTime&=&^double(double&rate,&double&time)&{&&&&&&&&&&&&&return&rate&*&&&&&&&&&&};&&&&&&&&&&&&&&&&&&double&dx&=&distanceFromRateAndTime(35,&1.5);&&&&&&&&&&&NSLog(@&A&car&driving&35&mph&will&travel&&&&&&&&&&&&&&&&&@&%.2f&miles&in&1.5&hours.&,&dx);&&&&&}&&&&&return&0;&}&
&在上面的代码中,利用插入符(^)将distanceFromRateAndTime变量标记为一个block。就像声明函数一样,需要包含返回值的类型,以及参数的类型,这样编译器才能安全的进行强制类型转换。插入符(^)跟指针(例如 int *aPointer)前面的星号(*)类似&&只是在声明的时候需要使用,之后用法跟普通的变量一样。
block的定义本质上跟函数一样&&只不过不需要函数名。block以签名字符串开始:^double(double rate, double time)标示返回一个double,以及接收两个同样为double的参数(如果不需要返回值,可以忽略掉)。在签名后面是一个大括弧({}),在这个括弧里面可以编写任意的语句代码,这跟普通的函数一样。
当把block赋值给distanceFromRateAndTime后,我们就可以像调用函数一样调用这个变量了。
不带参数的Block
如果block不需要任何的参数,那么可以忽略掉参数列表。另外,在定义block的时候,返回值的类型也是可选的,所以这样情况下,block可以简写为^ { & }:
double&(^randomPercent)(void)&=&^&{&&&&&return&(double)arc4random()&/&;&};&NSLog(@&Gas&tank&is&%.1f%%&full&,&&&&&&&randomPercent()&*&100);&
在上面的代码中,利用内置的arc4random()方法返回一个32位的整型随机数&&为了获得0-1之间的一个值,通过除以arc4random()方法能够获取到的最大值()。
到现在为止,block看起来可能有点像利用一种复杂的方式来定义一个方法。事实上,block是被设计为闭包的(closure)&&这就提供了一种新的、令人兴奋的编程方式。
Block的闭包性(closure)
在block内部,可以像普通函数一样访问数据:局部变量、传递给block的参数,全局变量/函数。并且由于block具有闭包性,所以还能访问非局部变量(non-local variable)。非局部变量定义在block之外,但是在block内部有它的作用域。例如,getFullCarName可以使用定义在block前面的make变量:
NSString&*make&=&@&Honda&;&NSString&*(^getFullCarName)(NSString&*)&=&^(NSString&*model)&{&&&&&return&[make&stringByAppendingFormat:@&&%@&,&model];&};&NSLog(@&%@&,&getFullCarName(@&Accord&));&&&&&
非局部变量会以const变量被拷贝并存储到block中,也就是说block对其是只读的。如果尝试在block内部给make变量赋值,会抛出编译器错误。
以const拷贝的方式访问非局部变量,意味着block实际上并不是真正的访问了非局部变量&&只不过在block中创建了非局部变量的一个快照。当定义block时,无论非局部变量的值是什么,都将被冻结,并且block会一直使用这个值,即使在之后的代码中修改了非局部变量的值。下面通过代码来看看,在创建好block之后,修改make变量的值,会发生什么:
NSString&*make&=&@&Honda&;&NSString&*(^getFullCarName)(NSString&*)&=&^(NSString&*model)&{&&&&&return&[make&stringByAppendingFormat:@&&%@&,&model];&};&NSLog(@&%@&,&getFullCarName(@&Accord&));&&&&&&&&make&=&@&Porsche&;&NSLog(@&%@&,&getFullCarName(@&911&Turbo&));&&
block的闭包性为block与上下文交互的时候带来极大的便利性,当block需要额外的数据时,可以避免使用参数&&只需要简单的使用非局部变量即可。
修改非局部变量
冻结中的非局部变量是一个常量值,这也是一种默认的安全行为&&因为这可以防止在block中的代码对非局部变量做了意外的修改。那么如果我们希望在block中对非局部变量值进行修改要如何做呢&&用__block存储修饰符(storage modifier)来声明非局部变量:
__block&NSString&*make&=&@&Honda&;&
这将告诉block对非局部变量做引用处理,在block外部make变量和内部的make变量创建一个直接的链接(direct link)。现在就可以在block外部修改make,然后反应到block内部,反过来,也是一样。
通过引用的方式访问非局部变量
这跟普通函数中的类似,用__block修饰符声明的变量可以记录着block多次调用的结果。例如下面的代码创建了一个block,在block中对i进行累加。
__block&int&i&=&0;&int&(^count)(void)&=&^&{&&&&&i&+=&1;&&&&&return&i;&};&NSLog(@&%d&,&count());&&&&&NSLog(@&%d&,&count());&&&&&NSLog(@&%d&,&count());&&&&&
Block作为函数的参数
把block存储在变量中有时候非常有用,比如将其用作函数的参数。这可以解决类似函数指针能解决的问题,不过我们也可以定义内联的block,这样代码更加易读。
例如下面Car interface中声明了一个方法,该方法用来计算汽车的里程数。这里并没有强制要求调用者给该方法传递一个常量速度,相反可以改方法接收一个block&&该block根据具体的时间来定义汽车的速度。
&#import&&Foundation/Foundation.h&&&&@interface&Car&:&NSObject&&&@property&double&&&&-&(void)driveForDuration:(double)duration&&&&&&&&withVariableSpeed:(double&(^)(double&time))speedFunction&&&&&&&&&&&&&&&&&&&&steps:(int)numS&&&@end&
上面代码中block的数据类型是double (^)(double time),也就是说block的调用者需要传递一个double类型的参数,并且该block的返回值为double类型。注意:上面代码中的语法基本与本文开头介绍的block变量声明相同,只不过没有变量名字。
在函数的实现里面可以通过speedFunction来调用block。下面的示例通过算法计算出汽车行驶的大约距离。其中steps参数是由调用者确定的一个准确值。
&#import&&Car.h&&&&@implementation&Car&&&@synthesize&odometer&=&_&&&-&(void)driveForDuration:(double)duration&&&&&&&&withVariableSpeed:(double&(^)(double&time))speedFunction&&&&&&&&&&&&&&&&&&&&steps:(int)numSteps&{&&&&&double&dt&=&duration&/&numS&&&&&for&(int&i=1;&i&=numS&i++)&{&&&&&&&&&_odometer&+=&speedFunction(i*dt)&*&&&&&&}&}&&&@end&
在下面的代码中,有一个main函数,在main函数中block定义在另一个函数的调用过程中。虽然理解其中的语法需要话几秒钟时间,不过这比起另外声明一个函数,再定义withVariableSpeed参数要更加直观。
&#import&&Foundation/Foundation.h&&#import&&Car.h&&&&int&main(int&argc,&const&char&*&argv[])&{&&&&&@autoreleasepool&{&&&&&&&&&Car&*theCar&=&[[Car&alloc]&init];&&&&&&&&&&&&&&&&&&&&[theCar&driveForDuration:10.0&&&&&&&&&&&&&&&&withVariableSpeed:^(double&time)&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&return&5.0;&&&&&&&&&&&&&&&&&&&&&&&&}&steps:100];&&&&&&&&&NSLog(@&The&car&has&now&driven&%.2f&meters&,&theCar.odometer);&&&&&&&&&&&&&&&&&&&&[theCar&driveForDuration:10.0&&&&&&&&&&&&&&&&withVariableSpeed:^(double&time)&{&&&&&&&&&&&&&&&&&&&&&&&&&&&&return&time&+&5.0;&&&&&&&&&&&&&&&&&&&&&&&&}&steps:100];&&&&&&&&&NSLog(@&The&car&has&now&driven&%.2f&meters&,&theCar.odometer);&&&&&}&&&&&return&0;&}&
上面利用一个简单的示例演示了block的通用性。在iOS的SDK中有许多API都利用了block的其它一些功能。NSArray的sortedArrayUsingComparator:方法可以使用一个block对元素进行排序,而UIView的animateWithDuration:animations:方法使用了一个block来定义动画的最终状态。此外,block在并发编程中具有强大的作用。
定义Block类型
由于block数据类型的语法会很快把函数的声明搞得难以阅读,所以经常使用typedef对block的签名(signature)做处理。例如,下面的代码创建了一个叫做SpeedFunction的新类型,这样我们就可以对withVariableSpeed参数使用一个更加有语义的数据类型。
&#import&&Foundation/Foundation.h&&&&&typedef&double&(^SpeedFunction)(double);&&&@interface&Car&:&NSObject&&&@property&double&&&&-&(void)driveForDuration:(double)duration&&&&&&&&withVariableSpeed:(SpeedFunction)speedFunction&&&&&&&&&&&&&&&&&&&&steps:(int)numS&&&@end&
许多标准的Objective-C框架也使用了这样的技巧,例如NSComparator。
Block不仅提供了C函数同样的功能,而且block看起来更加直观。block可以定义为内联(inline),这样在函数内部调用的时候就非常方便,由于block具有闭包性(closure),所以block可以很容易获得上下文信息,而又不会对这些数据产生负面影响。
CocoaChina是全球最大的苹果开发中文社区,官方微信每日定时推送各种精彩的研发教程资源和工具,介绍app推广营销经验,最新企业招聘和外包信息,以及Cocos2d引擎、Cocos Studio开发工具包的最新动态及培训信息。关注微信可以第一时间了解最新产品和服务动态,微信在手,天下我有!
请搜索微信号“CocoaChina”关注我们!
关注微信 每日推荐
扫一扫 浏览移动版block的创建,不带参数的block,block闭包,修改非局部变量,block作为函数参数,定义block类型,block的风险以及解决方法 - 简书
下载简书移动应用
写了92426字,被889人关注,获得了362个喜欢
block的创建,不带参数的block,block闭包,修改非局部变量,block作为函数参数,定义block类型,block的风险以及解决方法
block可以当做匿名函数,可以在两个对象间将语句当做数据来进行传递。具有封闭性closure,方便取得上下文相关状态信息。
Block的创建
可以如声明函数那样声明一个block变量
定义函数的方法定义block
把block当做一个函数来调用
int main(int argc, const char * argv[]) {
@autoreleasepool {
// Declare the block variable
double (^distanceFromRateAndTime)(double rate, double time);
// Create and assign the block
distanceFromRateAndTime = ^double(double rate, double time) {
return rate *
// Call the block
double dx = distanceFromRateAndTime(35, 1.5);
NSLog(@"A car driving 35 mph will travel "
@"%.2f miles in 1.5 hours.", dx);
不带参数的Block
block可以简写为^ { … }
double (^randomPercent)(void) = ^ {
return (double)arc4random() / ;
NSLog(@"Gas tank is %.1f%% full”, randomPercent() * 100);
Block的闭包(closure)
block内部可以访问定义在block外部的非局部变量。非局部变量会以拷贝形式存储到block中。
NSString *make = @"Honda";
NSString *(^getFullCarName)(NSString *) = ^(NSString *model) {
return [make stringByAppendingFormat:@" %@", model];
NSLog(@"%@", getFullCarName(@"Accord")); // Honda Accord
// Try changing the non-local variable (it won't change the block)
make = @"Porsche";
NSLog(@"%@", getFullCarName(@"911 Turbo")); // Honda 911 Turbo
修改非局部变量
用__block存储修饰符号(storage modifier)声明非局部变量
__block int i = 0;
int (^count)(void) = ^ {
NSLog(@"%d", count()); // 1
NSLog(@"%d", count()); // 2
NSLog(@"%d", count()); // 3
Block作为函数的参数
@interface Car : NSObject
- (void)driveForDuration:(double)duration
withVariableSpeed:(double (^)(double time))speedFunction
steps:(int)numS
//调用block
#import "Car.h"
@implementation Car
@synthesize odometer = _
- (void)driveForDuration:(double)duration
withVariableSpeed:(double (^)(double time))speedFunction
steps:(int)numSteps {
double dt = duration / numS
for (int i=1; i&=numS i++) {
_odometer += speedFunction(i*dt) *
//在main函数中block定义在另一个函数的调用过程中。
#import "Car.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Car *theCar = [[Car alloc] init];
// Drive for awhile with constant speed of 5.0 m/s
[theCar driveForDuration:10.0
withVariableSpeed:^(double time) {
return 5.0;
} steps:100];
NSLog(@"The car has now driven %.2f meters", theCar.odometer);
// Start accelerating at a rate of 1.0 m/s^2
[theCar driveForDuration:10.0
withVariableSpeed:^(double time) {
return time + 5.0;
} steps:100];
NSLog(@"The car has now driven %.2f meters", theCar.odometer);
定义Block类型
// Define a new type for the block
typedef double (^SpeedFunction)(double);
@interface Car : NSObject
- (void)driveForDuration:(double)duration
withVariableSpeed:(SpeedFunction)speedFunction
steps:(int)numS
block会存在导致retain cycles的风险,如果发送者需要 retain block 但又不能确保引用在什么时候被赋值为 nil, 那么所有在 block 内对 self 的引用就会发生潜在的 retain 环。NSOperation 是使用 block 的一个好范例。因为它在一定的地方打破了 retain 环,解决了上述的问题。
self.queue = [[NSOperationQueue alloc] init];
MyOperation *operation = [[MyOperation alloc] init];
pletionBlock = ^{
[self finishedOperation];
[self.queue addOperation:operation];
另一个解决方法
@interface Encoder ()
@property (nonatomic, copy) void (^completionHandler)();
@implementation Encoder
- (void)encodeWithCompletionHandler:(void (^)())handler
pletionHandler =
// 进行异步处理...
// 这个方法会在完成后被调用一次
- (void)finishedEncoding
pletionHandler();
pletionHandler = //一旦任务完成就设置为nil
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮
被以下专题收入,发现更多相似内容:
技术的路上,永远都是菜鸟。
· 46人关注
· 8人关注
如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
选择支付方式:

我要回帖

更多关于 block里面的外部变量 的文章

 

随机推荐