IOS开发之Block 编程(翻译官方文档)
2014-01-08 13:09
387 查看
本文转自:http://blog.csdn.net/perfect_promise/article/details/7757746
注:小弟才疏学浅,英文水平够烂,若有不正确或误导的地方,请大家指出,欢迎大家指正和修改。本文中涉及的词法范围:作用范围,例如if{}else{},两个{}分别是if和else的作用范围。
你可以用Block来写一些可以传到API中的函数语句,可选择性地存储,并可以使用多线程。作为一个回调,Block特别的有用,因为block既包含了回调期间的代码,又包含了执行期间需要的数据。
作为Mac OS X v10.6Xcode开发工具附带的工具,Block在GCC和Clang中同样可用。你能在Mac OS X v10.6及其以上版本和iOS 4.0及其以上版本中使用Block。.Block的运行是开源的,因此你能在LLVM’s
compiler-rtsubprojectrepository里面找到它。Block也已经被提交到C标准工作组作为 N1370:
Apple’sExtensionstoC。由于Objective-C和C++都是衍生自C,block被设计为可同时兼容这三种语言。
你应该阅读这篇文档,去学习Block是什么,以及在C、C++和OC中如何使用Block使你的程序更加的高效和更易于维护。
注意:Block可以使用定义范围之内的任何变量。
如果你把Block声明为一个变量,你以后就可以像调用一个方法一样使用它:
Block的一个强大的特性是,它能在相同的词法范围内修改变量值。Block能修改变量是通过_block存储类型标示符。与 “Cocoa
Block” 中的例子一样,你能使用一个block变量来计算有多少字符串与下面例子中是相同的。Block直接被用,并且用
更详细的内容请查看 “Blocks
和变量.”
与方法一样,有一个类型参数列表
有一个隐形或声明的返回类型
能从它定义的词法范围内获取状态
能有选择性地修改词法范围中的状态
能共享相同词法范围内其他块定义的修改的潜在性
词法范围被销毁后仍能继续在已定义的词法范围内共享和修改状态
你能复制一个block,并把它传递给其他线程来延迟执行(或者,在它自己的线程内,做一个运行环)。编译和运行过程中,从block中引用的所有变量都保留乐一份block的副本。block不仅适用于纯C和C++,同时block也是一个Objective-C对象。
Blocks之所以能替代传统的回调方法主要有以下两个理由:
它允许你在调用点写代码,调用点稍后会在方法实现段被执行。
Blocks通常也是框架方法中的参数。
它允许访问局部变量。
与其使用回调,需要一个包含所有上下文信息的数据结构,你只需要执行一个操作,直接访问局部变量即可。
Block支持可变参数(
你可以创建一个block类型,当你在多个地方使用到同一个签名的block时,这种方式时很好的。
如果你不显式声明块表达式的返回值,它可以根据block的内容进行自动匹配。如果返回类型和参数列表都是void,你也可以省略参数列表。 如果存在多个返回语句,应该正确的进行匹配(又需要的话,可以使用类型转换)。
与函数一样,block支持三种标准类型的变量:
全局变量,带有static修饰符的变量
全局函数(不是专门的变量)
局部变量和参数
Blocks也支持其他两种变量类型:
函数级别的_block变量。如有引用块被复制到堆,block中的_block变量是可变的。
最后,在一个方法的实现,块可能引用的Objective-C的实例变量—参考“Object
andBlockVariables.”
以下是在block中使用变量的规则:
可以访问全局变量,包括词法范围内存在的static变量。
可以传参给block,与传参给函数的方式是一样的。
局部词法范围内的堆栈变量被看成时const变量。
他们的值存放在程序内的block语句中。在嵌套block中,他们的值来自于最近的词法范围内。
声明为_block类型的
局部词法范围内的变量是可改变的。更改的适用范围仅为局部词法范围,包括词法范围内定义的其他block。在“The
__blockStorageType.”中有更详细的描述。
block作用范围内声明的局部变量,与函数中的局部变量一样。
block的每一次调用,都重新生成此变量的新的副本。这些变量可以被转换为const或引用变量在块内的作用域。
下面的例子说明了局部非静态变量的使用:
如上所述,在block内试图分配一个新的x的值将会报错:
为了使一个变量在block内部可以被修改,你应该使用_block来修饰这个变量—参考 “The
__blockStorageType.”
作为优化,在堆栈上的块存储块启动就像自身调用一样。如果块被复制,使用Block_copy(或在Objective-C中块发送一个副本),变量将被复制到堆。因此,_block块的地址可以随时更改。
_block变量有两个进一步的限制:他们不能是可变数组,不能包含C99的可变长数组的结构。
下面的例子说明了_block变量的作用:
下面的例子显示了几种类型的变量块的相互作用:
在手动引用计数的环境,复制块时,块内使用局部变量保留。Block内使用的局部变量引用技术将retain。如果您想覆盖一个特定对象变量的这种行为,你可以标记_block修饰符来修饰该变量。
如果您使用ARC,当block被copy时对象变量被保留,并自动释放,和延迟释放。
注:在垃圾收集的环境,如果你给变量同时使用_weak和_block修饰符,那么该block将无法确保是否还存在。
如果你在执行方法内使用block,实例变量对象的内存管理规则更加微妙:
如果您访问实例变量的参照,对象retain;
如果您访问实例变量的值,对象retain;
下面的例子说明了两种不同的情况:
然而,通常情况下,你使用block作为一个函数或方法的参数,在这些情况下,你通常创建一个块“内联”。
请注意,该块包含在函数的参数列表。
下面的示例显示如何使用block的dispatch_apply函数。
功能是提交一个block到一个调度队列进行多次调用。它携带了三个参数;第一个参数指定执行的迭代的数量;第二个参数指定block被提交到哪个队列;点歌参数就是block自身,反过来这需要一个参数迭代的当前索引。
可以使用
下面的示例,确定一个数组前五个元素中任意一个在过滤集中的索引数。
下方的例子是确定一个NSSet对象中是否包含有局部变量指定的一个单词,如果包含的话,设置另一个局部变量的值为YES。found也被声明为一个_block变量, 这个block是定义联:
你能用C函数来复制和释放block:
如果你使用Objective-C,block的属性可以使用copy、retain、release和autorelease。
为了避免产生内存泄露,block的copy和retain的使用必须平衡。使用了copy和retain的地方必须进行release(autorelease除外)——除非在一个垃圾收集环境。
如果你想传一个C字符串值,你必须使用引用。例如,把这个字符串传到
注:小弟才疏学浅,英文水平够烂,若有不正确或误导的地方,请大家指出,欢迎大家指正和修改。本文中涉及的词法范围:作用范围,例如if{}else{},两个{}分别是if和else的作用范围。
介绍
Block对象是一个C级别的语法和运行机制。它与标准的C函数类似,不同之处在于,它除了有可执行代码以外,它还包含了与堆、栈内存绑定的变量。因此,Block对象包含着一组状态数据,这些数据在程序执行时用于对行为产生影响。你可以用Block来写一些可以传到API中的函数语句,可选择性地存储,并可以使用多线程。作为一个回调,Block特别的有用,因为block既包含了回调期间的代码,又包含了执行期间需要的数据。
作为Mac OS X v10.6Xcode开发工具附带的工具,Block在GCC和Clang中同样可用。你能在Mac OS X v10.6及其以上版本和iOS 4.0及其以上版本中使用Block。.Block的运行是开源的,因此你能在LLVM’s
compiler-rtsubprojectrepository里面找到它。Block也已经被提交到C标准工作组作为 N1370:
Apple’sExtensionstoC。由于Objective-C和C++都是衍生自C,block被设计为可同时兼容这三种语言。
你应该阅读这篇文档,去学习Block是什么,以及在C、C++和OC中如何使用Block使你的程序更加的高效和更易于维护。
声明和使用Block
用^操作符来声明一个Block变量,并指明Block述句的开始。Block的主体部分包含在{}内,像下面的例子中一样(与C语法一样,“;”指明语句的结束):
intmultiplier=7; |
int(^myBlock)(int)=^(intnum){ |
returnnum*multiplier; |
}; |
注意:Block可以使用定义范围之内的任何变量。
如果你把Block声明为一个变量,你以后就可以像调用一个方法一样使用它:
intmultiplier=7; |
int(^myBlock)(int)=^(intnum){ |
returnnum*multiplier; |
}; |
printf("%d",myBlock(3)); |
//prints"21" |
直接使用Block
很多情况下,你不需要声明Block变量;你只是简单地写一个Block语句内联在需要使用它作为参数的地方。下面的例子使用了qsort_b方法,qsort_b方法与标准的qsort_r类似,只是用Block作为它的最后一个参数。
char*myCharacters[3]={"TomJohn","George","CharlesCondomine"}; |
qsort_b(myCharacters,3,sizeof(char*),^(constvoid*l,constvoid*r){ |
char*left=*(char**)l; |
char*right=*(char**)r; |
returnstrncmp(left,right,1); |
}); |
//myCharactersisnow{"CharlesCondomine","George","TomJohn"} |
CocoaBlock
Cocoa框架中有几个方法使用Block作为参数,通常是在执行对象的操作集合,或者操作完成后使用它作为回调。下面的例子向我们展示了在NSArray对象的方法
.。这个方法只有单一的参数,block被定义为sortedArrayUsingComparator:怎样使用Block
局部变量:NSComparator
NSArray*stringsArray=[NSArrayarrayWithObjects: |
@"string1", |
@"String21", |
@"string12", |
@"String11", |
@"String02",nil]; |
staticNSStringCompareOptionscomparisonOptions=NSCaseInsensitiveSearch|NSNumericSearch| |
NSWidthInsensitiveSearch|NSForcedOrderingSearch; |
NSLocale*currentLocale=[NSLocalecurrentLocale]; |
NSComparatorfinderSortBlock=^(idstring1,idstring2){ |
NSRangestring1Range=NSMakeRange(0,[string1length]); |
return[string1compare:string2options:comparisonOptionsrange:string1Rangelocale:currentLocale]; |
}; |
NSArray*finderSortArray=[stringsArraysortedArrayUsingComparator:finderSortBlock]; |
NSLog(@"finderSortArray:%@",finderSortArray); |
/* |
Output: |
finderSortArray:( |
"string1", |
"String02", |
"String11", |
"string12", |
"String21" |
) |
*/ |
_block变量
Block的一个强大的特性是,它能在相同的词法范围内修改变量值。Block能修改变量是通过_block存储类型标示符。与 “CocoaBlock” 中的例子一样,你能使用一个block变量来计算有多少字符串与下面例子中是相同的。Block直接被用,并且用
currentLocale作为一个只读变量在block中。
NSArray*stringsArray=[NSArrayarrayWithObjects: |
@"string1", |
@"String21",//<- |
@"string12", |
@"String11", |
@"Strîng21",//<- |
@"Striñg21",//<- |
@"String02",nil]; |
NSLocale*currentLocale=[NSLocalecurrentLocale]; |
__blockNSUIntegerorderedSameCount=0; |
NSArray*diacriticInsensitiveSortArray=[stringsArraysortedArrayUsingComparator:^(idstring1,idstring2){ |
NSRangestring1Range=NSMakeRange(0,[string1length]); |
NSComparisonResultcomparisonResult=[string1compare:string2options:NSDiacriticInsensitiveSearchrange:string1Rangelocale:currentLocale]; |
if(comparisonResult==NSOrderedSame){ |
orderedSameCount++; |
} |
returncomparisonResult; |
}]; |
NSLog(@"diacriticInsensitiveSortArray:%@",diacriticInsensitiveSortArray); |
NSLog(@"orderedSameCount:%d",orderedSameCount); |
/* |
Output: |
diacriticInsensitiveSortArray:( |
"String02", |
"string1", |
"String11", |
"string12", |
"String21", |
"Str\U00eeng21", |
"Stri\U00f1g21" |
) |
orderedSameCount:2 |
*/ |
和变量.”
Block功能
Block是一个匿名的内嵌代码集:与方法一样,有一个类型参数列表
有一个隐形或声明的返回类型
能从它定义的词法范围内获取状态
能有选择性地修改词法范围中的状态
能共享相同词法范围内其他块定义的修改的潜在性
词法范围被销毁后仍能继续在已定义的词法范围内共享和修改状态
你能复制一个block,并把它传递给其他线程来延迟执行(或者,在它自己的线程内,做一个运行环)。编译和运行过程中,从block中引用的所有变量都保留乐一份block的副本。block不仅适用于纯C和C++,同时block也是一个Objective-C对象。
用法
Blocks通常表示比较小的,独立的代码段。因此,它特别适用于可能被同时执行的封装单元工作的模式,或者是集合中的项目,或者是当另一个操作完成后的一个回调。Blocks之所以能替代传统的回调方法主要有以下两个理由:
它允许你在调用点写代码,调用点稍后会在方法实现段被执行。
Blocks通常也是框架方法中的参数。
它允许访问局部变量。
与其使用回调,需要一个包含所有上下文信息的数据结构,你只需要执行一个操作,直接访问局部变量即可。
声明block参考
Block变量持有Block引用。声明它的语法与在函数中声明指针类似,用^代替
*。其余部分,与C类型系统,具有完全的互操作性。以下时所有有效块的变量声明:
void(^blockReturningVoidWithVoidArgument)(void); |
int(^blockReturningIntWithIntAndCharArguments)(int,char); |
void(^arrayOfTenBlocksReturningVoidWithIntArgument[10])(int); |
...)参数。不带任何参数的block,在参数列表中必须指定为void。Block的设计考虑到类型安全,通过提供给编译器全套的元数据来验证block的使用、传递参数到block中和返回值分配。一个块引用可以转换到任意类型的指针,反之亦然。但是,你不能通过*来获得block的值,因而在编译时,block的大小也不能被计算出来。
你可以创建一个block类型,当你在多个地方使用到同一个签名的block时,这种方式时很好的。
typedeffloat(^MyBlockType)(float,float); |
MyBlockTypemyFirstBlock=//...; |
MyBlockTypemySecondBlock=//...; |
创建block
用^指明block语句的开始。在它后面的()是参数列表。block的主体部分在{}里面.。下面的例子定义了一个简单的block,并把先前定义的变量(oneForm)分配给它。int(^oneFrom)(int); |
oneFrom=^(intanInt){ |
returnanInt-1; |
}; |
全局block
在文件级别,你可以使用block作为一个全局表达式。#import<stdio.h> |
intGlobalInt=0; |
int(^getGlobalInt)(void)=^{returnGlobalInt;}; |
变量类型
block对象的代码中,变量被看成五种不同的方式。与函数一样,block支持三种标准类型的变量:
全局变量,带有static修饰符的变量
全局函数(不是专门的变量)
局部变量和参数
Blocks也支持其他两种变量类型:
函数级别的_block变量。如有引用块被复制到堆,block中的_block变量是可变的。
const
最后,在一个方法的实现,块可能引用的Objective-C的实例变量—参考“Object
andBlockVariables.”
以下是在block中使用变量的规则:
可以访问全局变量,包括词法范围内存在的static变量。
可以传参给block,与传参给函数的方式是一样的。
局部词法范围内的堆栈变量被看成时const变量。
他们的值存放在程序内的block语句中。在嵌套block中,他们的值来自于最近的词法范围内。
声明为_block类型的
局部词法范围内的变量是可改变的。更改的适用范围仅为局部词法范围,包括词法范围内定义的其他block。在“The
__blockStorageType.”中有更详细的描述。
block作用范围内声明的局部变量,与函数中的局部变量一样。
block的每一次调用,都重新生成此变量的新的副本。这些变量可以被转换为const或引用变量在块内的作用域。
下面的例子说明了局部非静态变量的使用:
intx=123; |
void(^printXAndY)(int)=^(inty){ |
printf("%d%d\n",x,y); |
}; |
printXAndY(456);//prints:123456 |
intx=123; |
void(^printXAndY)(int)=^(inty){ |
x=x+y;//error |
printf("%d%d\n",x,y); |
}; |
__blockStorageType.”
_block存储类型
在变量前面加上_block类型修饰符,我们可以指定传进来的变量是可变或者可读写的。_block存储与它类似,但是局部变量的寄存器、auto变量和static存储类型之间相互排斥。_block变量共享变量之间的作用域和块之间的作用域拷贝变量存储范围内。因此,如果block中定义的所有拷贝在框架内的生存超越帧结束(例如,正在排队等待执行),堆栈帧被破坏后存储也将继续存在。在一个给定的词法范围的多个块,可以同时使用共享变量。
作为优化,在堆栈上的块存储块启动就像自身调用一样。如果块被复制,使用Block_copy(或在Objective-C中块发送一个副本),变量将被复制到堆。因此,_block块的地址可以随时更改。
_block变量有两个进一步的限制:他们不能是可变数组,不能包含C99的可变长数组的结构。
下面的例子说明了_block变量的作用:
__blockintx=123;//xlivesinblockstorage |
void(^printXAndY)(int)=^(inty){ |
x=x+y; |
printf("%d%d\n",x,y); |
}; |
printXAndY(456);//prints:579456 |
//xisnow579 |
externNSIntegerCounterGlobal; |
staticNSIntegerCounterStatic; |
{ |
NSIntegerlocalCounter=42; |
__blockcharlocalCharacter; |
void(^aBlock)(void)=^(void){ |
++CounterGlobal; |
++CounterStatic; |
CounterGlobal=localCounter;//localCounterfixedatblockcreation |
localCharacter='a';//setslocalCharacterinenclosingscope |
}; |
++localCounter;//unseenbytheblock |
localCharacter='b'; |
aBlock();//executetheblock |
//localCharacternow'a' |
} |
对象和block变量
Block提供支持的Objective-C和C++的对象,和其他块,作为变量。
Objective-C对象
在手动引用计数的环境,复制块时,块内使用局部变量保留。Block内使用的局部变量引用技术将retain。如果您想覆盖一个特定对象变量的这种行为,你可以标记_block修饰符来修饰该变量。如果您使用ARC,当block被copy时对象变量被保留,并自动释放,和延迟释放。
注:在垃圾收集的环境,如果你给变量同时使用_weak和_block修饰符,那么该block将无法确保是否还存在。
如果你在执行方法内使用block,实例变量对象的内存管理规则更加微妙:
如果您访问实例变量的参照,对象retain;
如果您访问实例变量的值,对象retain;
下面的例子说明了两种不同的情况:
dispatch_async(queue,^{ |
//instanceVariableisusedbyreference,selfisretained |
doSomethingWithObject(instanceVariable); |
}); |
idlocalVariable=instanceVariable; |
dispatch_async(queue,^{ |
//localVariableisusedbyvalue,localVariableisretained(notself) |
doSomethingWithObject(localVariable); |
}); |
调用block
如果你声明block作为一个变量,你可以像使用函数一样使用它,就像下面两个示例所示一样:int(^oneFrom)(int)=^(intanInt){ |
returnanInt-1; |
}; |
printf("1from10is%d",oneFrom(10)); |
//Prints"1from10is9" |
float(^distanceTraveled)(float,float,float)= |
^(floatstartingSpeed,floatacceleration,floattime){ |
floatdistance=(startingSpeed*time)+(0.5*acceleration*time*time); |
returndistance; |
}; |
floathowFar=distanceTraveled(0.0,9.8,1.0); |
//howFar=4.9 |
使用block作为函数参数
可以把block作为函数参数进行传递,就像其他参数一样。然后,很多时候你不需要声明block;而你只需把他们内联到需要使用block作为一个参数的地方。下面的例子使用了qsort_b方法,qsort_b方法与标准的qsort_r类似,只是用Block作为它的最后一个参数。
char*myCharacters[3]={"TomJohn","George","CharlesCondomine"}; |
qsort_b(myCharacters,3,sizeof(char*),^(constvoid*l,constvoid*r){ |
char*left=*(char**)l; |
char*right=*(char**)r; |
returnstrncmp(left,right,1); |
}); |
//Blockimplementationendsat"}" |
//myCharactersisnow{"CharlesCondomine","George","TomJohn"} |
下面的示例显示如何使用block的dispatch_apply函数。
dispatch_apply
使用以下方式进行定义:
voiddispatch_apply(size_titerations,dispatch_queue_tqueue,void(^block)(size_t)); |
可以使用
dispatch_apply分别打印出迭代索引,如下所示:
#include<dispatch/dispatch.h> |
size_tcount=10; |
dispatch_queue_tqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0); |
dispatch_apply(count,queue,^(size_ti){ |
printf("%u\n",i); |
}); |
使用block作为方法参数
Cocoa提供了一种方法,使用blocks。传递block作为参数与传递其他参数的方式是一样的。下面的示例,确定一个数组前五个元素中任意一个在过滤集中的索引数。
NSArray*array=[NSArrayarrayWithObjects:@"A",@"B",@"C",@"A",@"B",@"Z",@"G",@"are",@"Q",nil]; |
NSSet*filterSet=[NSSetsetWithObjects:@"A",@"Z",@"Q",nil]; |
BOOL(^test)(idobj,NSUIntegeridx,BOOL*stop); |
test=^(idobj,NSUIntegeridx,BOOL*stop){ |
if(idx<5){ |
if([filterSetcontainsObject:obj]){ |
returnYES; |
} |
} |
returnNO; |
}; |
NSIndexSet*indexes=[arrayindexesOfObjectsPassingTest:test]; |
NSLog(@"indexes:%@",indexes); |
/* |
Output: |
indexes:<NSIndexSet:0x10236f0>[numberofindexes:2(in2ranges),indexes:(03)] |
*/ |
__blockBOOLfound=NO; |
NSSet*aSet=[NSSetsetWithObjects:@"Alpha",@"Beta",@"Gamma",@"X",nil]; |
NSString*string=@"gamma"; |
[aSetenumerateObjectsUsingBlock:^(idobj,BOOL*stop){ |
if([objlocalizedCaseInsensitiveCompare:string]==NSOrderedSame){ |
*stop=YES; |
found=YES; |
} |
}]; |
//Atthispoint,found==YES |
复制block
通常情况下,你不需要复制(或保留)一个块。如果你想要block在它的定义域被销毁后仍可以被使用,你仅仅只需要创建一个副本。复制移动block到堆中。.你能用C函数来复制和释放block:
Block_copy(); |
Block_release(); |
为了避免产生内存泄露,block的copy和retain的使用必须平衡。使用了copy和retain的地方必须进行release(autorelease除外)——除非在一个垃圾收集环境。
避免的模式
块文本(即,^{...})是一个堆栈的本地数据结构的地址块。因此堆栈的本地数据结构的范围是封闭的复合语句,所以你应该避免使用下例中的模式:,voiddontDoThis(){ |
void(^blockArray[3])(void); //anarrayof3blockreferences |
for(inti=0;i<3;++i){ |
blockArray[i]=^{printf("hello,%d\n",i);}; |
//WRONG:Theblockliteralscopeisthe"for"loop |
} |
} |
voiddontDoThisEither(){ |
void(^block)(void); |
inti=random(): |
if(i>1000){ |
block=^{printf("gotiat:%d\n",i);}; |
//WRONG:Theblockliteralscopeisthe"then"clause |
} |
//... |
} |
调试
在block中你可以设置断点进行单步调试。你可以从调用块内GDB会议调用一个block,如下例所示:$invoke-blockmyBlock1020 |
doSomethingWithStringblock中,你可以像下面这样写:
$invoke-blockdoSomethingWithString"\"thisstring\"" |
相关文章推荐
- iOS中的block(block编程官方文档翻译)
- iOS Threading编程指南 官方文档翻译第一篇(序言)
- Block 编程(翻译官方文档)
- Block 编程(翻译官方文档)
- IOS开发入门:官方文档 Auido Session Programming Guide 翻译
- 【Block编程第一篇】 block编程热点介绍(官方文档翻译的)
- 91.【block编程第一篇】 block编程热点介绍(官方文档翻译的)
- [iOS]Advanced Memory Management Programming Guide 高级内存管理编程指南(官方文档翻译)
- Block 编程(翻译官方文档)
- Mac OS, iOS多线程开发指南,apple官方文档翻译稿
- React Native开发环境配置(MAC + iOS)官方文档翻译总结
- 【iOS官方文档翻译】iOS的蓝牙连接、数据接收及发送
- iOS开发系列----UI(视图编程入门:Delegate、Block、单例、属性传值)
- 很多翻译的官方文档IOS
- Android开发之基类Object官方文档翻译
- IOS开发之Block编程
- iOS 英语学习 翻译官方文档--不定时更新。
- 蓝牙4.0——Android BLE开发官方文档翻译
- iOS7开发-Apple苹果iPhone开发Xcode官方文档翻译PDF下载地址(2014年2月19日更新版)
- iOS开发-Swift4译文Swift4官方文档中文版/ Basic Operators(上)(转载)