Block
Apple 在C, Objective-C, C++加上Block�@��延申用法。目前只有Mac 10.6 和iOS 4有支援。Block是由一堆可�绦械某淌浇M成,也可以�Q做�]有名字的Function (Anonymous function)。如果是Mac 10.6 或 iOS 4.0 之前的平台可以利用 http://code.google.com/p/plblocks/ �@��project得以支援Block�Z法。
Apple有一��叫做GCD(Grand Central Dispach)的新功能,用在同步�理(concurrency)的�h境下有更好的效率。Block�Z法�a生的��C就是�碜造�GCD,用Block包好 一��工作量交�oGCD,GCD有一��宏�^的�野可以�矸峙�CPU,GPU,Memory的�硐伦詈玫�Q定。
Block �介
Block其��行�楹�Function很像,最大的差�e是在可以存取同一��Scope的��抵怠�
Block ���w���L成�@��
^(�魅� ��盗� ) { 行�橹黧w };
Block���w�_�^是"^",接著是由小括�所包起�淼���盗�(比如 int a, int b, float c),行�榈闹黧w由大括�包起�恚��S忻��~叫做block literal。行�橹黧w可以用return回�髦担�型�e��被compiler自�愚k�R出�怼H绻��]有��盗幸��@���(void)。
看��列子
^( int a) { return a*a;};
�@是代表Block��回�鬏�入值的平方值(int a 就是��盗�,return a*a; 就是行�橹黧w)。�得主�w�e最後要加";"因�槭�⑹觯�而整��{}最後也要要加";"因��Block是��物件���w。
用法就是
int result = ^(int a) {return a*a ;} (5) ;
很怪吧。後面小括��e的5 ��被��成a的�入值然後�由Block�出5*5 = 25指定�oresult�@����怠�
有�]有��我稽c的方法不然每次都要���@�N�L?有。接下�硪�介�B一��叫Block Pointer的�|西�砗�化我��的��法。
Block Pointer是�@�有�告的
回�髦� (^名字 ) (��盗� );
直接�砜匆��列子
int (^ square) (int);
// 有一��叫square的Block Pointer,其所指向的Block是有一��int �入和 int �出
square = ^(int a ) {return a*a ;}; // �����Block ���w指定�o square
使用Block Pointer的例子
int result = square(5); // 感�X上不就是funtion的用法�幔�
也可以把Block Pointer��成���鹘o一��function,比如�f
void myFuction( int (^mySquare) (int) ); // function 的宣告,
�魅胍��有一��int�入和int�出的Block 型�e的���
呼叫�@��myFunction的�r候就是�@�雍艚�
int (^mySqaure) (int) = ^(int a) {return a*a;};
// 先�o好一��有���w的block pointer叫mySquare
myFunction( mySqaure ) ; //把mySquare�@��block pointer�omyFunction�@��function
或是不用block pointer 直接�o一��block ���w,就�@���
myFunction( ^(int a) {return a*a} ) ;
��成Objective-C method 的�魅胫档脑�都是要把型�e��在��登懊嫒会峒由闲±ㄌ�,因些���就要�@���
-(void) objcMethod:( int (^) (int) ) square; // square ��档男�e是 int (^) (int)
�x文至此是不是��Block有基本的�J�R? 接下�砦��要��Block相�P的行�楹吞厣�
首先是�砜匆幌略�Block�e面存取外部��档姆椒�
存取���
1. 可以�x取和Block pointer同一��scope的��抵担�
{
int outA = 8;
int (^myPtr) (int) = ^(int a) {return outA+a;};
// block �e面可以�x同一��scope的outA的值
int result = myPtr(3); // result is 11
}
我��再�砜匆��很有趣的例子
{
int outA = 8;
int (^myPtr) (int) = ^(int a) {return outA+a;};
// block �e面可以�x同一��scope的outA的值
outA = 5; // 在呼叫myPtr之前改�outA的值
int result = myPtr(3); // result 的值�是 11�K不是 8
}
事��上呢,myPtr在其主�w用到outA�@����抵档�r候是做了一��copy的�幼靼�outA的值copy下�怼K�以之後outA即使�Q了新的值��於myPtr�ecopy的值是�]有影�到的。
要注意的是,�@��指的值是��档闹担�如果�@����档闹凳且������w的位置,�Q句��f,�@����凳��pointer的�,它指到的值是可以在block�e被改�的。
{
NSMutableArray * mutableArray = [NSMutableArray arrayWithObjects:@"one",@"two",@"three",nil];
int result = ^(int a) { [mutableArray removeLastObject]; return a*a;} (5);
NSLog(@"test array %@", mutableArray);
}
原本mutableArray的值是{@"one",@"two",@"three"}在block�e被更改mutableArray所指向的物件後,mutableArray的值就��被成{@"one",@"two"}
2. 直接存取static 的���
{
static int outA = 8;
int (^myPtr) (int) = ^(int a) {return outA+a;};
// block �e面可以�x同一��scope的outA的值
outA = 5; // 在呼叫myPtr之前改�outA的值
int result = myPtr(3); // result 的值是 8,因��outA是��static ����直接反��其值
}
甚至可以在block�e面直接改�outA的值比如�@���
{
static int outA = 8;
int (^myPtr) (int) = ^(int a) { outA= 5; return outA+a;};
// block �e面改�outA的值
int result = myPtr(3); // result 的值是 8,因��outA是��static ����直接反��其值
}
3. Block Variable
在某����登懊嫒绻�加上修�字__block 的�(注意block前有���下底�),�@����涤址Q��block variable。那�N在block�e就可以任意修改此��抵担���抵档母淖�也可以知道。
{
__block int num = 5;
int (^myPtr) (int) = ^(int a) { return num++;};
int (^myPtr2) (int) = ^(int a) { return num++;};
int result = myPtr(0);
result = myPtr2(0);
}
因��myPtr和myPtr2都有用到num�@��block variable,最後result的值就��是7
生命周期和����w管理
因��block也是�^承自NSObject,所以其生命周期和����w的管理也就非常之重要。
block一�_始都是被放到stack�e,�Q句��f其生命周期�S著method或function�Y束就��被回收,和一般��档纳�命周期一�印�
�P於����w的管理�遵循�@���要�c
1. block pointer的���w��在method或function�Y束後就��被清掉
2. 如果要保存block pointer的���w要用-copy指令,�@��block pointer就��被放到heap�e
2.1 block 主�w�e用到的block variable 也��被搬到heap 而有新的����w位置,且一�K更新有用到�@��block variable 的block都指到新的位置
2.2 一般的variable值��被copy
2.3 如果主�w�e用到的variable是object的�,此object��被retain, block release�r也��被release
2.4 __block variable �e用到的object是不��被retain的
首先�砜匆幌逻@��例子
typedef int (^MyBlock)(int);
MyBlock genBlock();
int main(){
MyBlock outBlock = genBlock();
int result = outBlock(5);
NSLog(@"result is %d",[outBlock retainCount] ); // segmentation fault
NSLog(@"result is %d",result );
return 0 ;
}
MyBlock genBlock() {
int a = 3;
MyBlock inBlock = ^(int n) {
return n*a;
};
return inBlock ;
}
此程式由genBlock�e�a生的block再指定�omain function的outBlock��担��绦羞@��程式��得到
Segmentation fault
(�]:有�r候把 genBlock�e的a 去掉就可以跑出�Y果的情形,�@是系�ycache住����w,�K不是inBlock真得一直存在,久了�是��被回收,千�f不要以�槭�Φ��法)
表示我��用到了不�用的����w,在�@��例子的情�r下是在genBlock�e的inBlock��翟�return的�r候就被回收了,outBlock�o法有一��合法的����w位置-retainCount就�]意�x了。
如果�@���r候需要保留inBlock的值就要用-copy指令,��genBlock改成
MyBlock genBlock() {
int a = 3;
MyBlock inBlock = ^(int n) {
return n*a;
};
return [inBlock copy] ;
}
�@��[inBlock copy]的回�髦稻��被放到heap,就可以一直使用(�得要release)
�绦薪Y果是
result is 1
result is 15
再次提醒要�得release outBlock。
如果一回��[inBlock copy]的值就不再需要的�r候可以�@���
MyBlock genBlock() {
int a = 3;
MyBlock inBlock = ^(int n) {
return n*a;
};
return [[inBlock copy] autorelease] ;
}
-copy指令是�榱艘�把block ��stack搬到heap,autorelease是�榱似叫nretainCount加到autorelease oop ,回�髦�後等到事件�Y束就清掉。
接下�硎�block存取到的local variable是��物件的型�e,然後做copy 指令�r
MyBlock genBlock() {
int a = 3;
NSMutableString * myString = [NSMutableString string];
MyBlock inBlock = ^(int n) {
NSLog(@"retain count of string %d",[myString retainCount]);
return n*a;
};
return [inBlock copy] ;
}
�Y果��印出
retain count of string 2
�@���Y果和上面2.3提到的一�樱�local variable被retain了
那再�碓��2.4,在local variable前面加上__block
MyBlock genBlock() {
int a = 3;
__block NSMutableString * myString = [NSMutableString string];
MyBlock inBlock = ^(int n) {
NSLog(@"retain count of string %d",[myString retainCount]);
return n*a;
};
return [inBlock copy] ;
}
�绦械慕Y果就是��
retain count of string 1
Block Copying注意事�
如果在Class method�e面做copying block�幼鞯脑�
1. 在Block�e如果有直接存取到self,�tself��被retain
2. 在Block�e如果取存到instance variable (�o�直接或是��accessor),�tself��被retain
3. 取存到local variable所�碛械�object�r,�@��object��被retain
�我���砜匆��自�的Class
@interface MyObject : NSObject {
NSString * title;
void (^ myLog) (NSString * deco);
}
-(void) logName;
@end
@implementation MyObject
-(id) initWithTitle:(NSString * ) newTitle{
if(self = [super init]){
title = newTitle;
myLog = [^(NSString * deco) {NSLog(@" %@%@%@",deco, title, deco ); } copy];
}
return self;
}
-(void) logName{
myLog(@"==");
}
-(void ) dealloc{
[myLog release];
[title release];
[super dealloc];
}
@end
在main �e使用如下
MyObject * mObj = [[MyObject alloc] initWithTitle:@"Car"];
NSLog(@"retainCount of MyObject is %d",[mObj retainCount] );
[mObj logName];
其�绦械慕Y果��
retainCount of MyObject is 2
==Car==
因�樵�MyObject的建��子�emyLog�@��block pointer用了title�@��instance variable然後就��retain self也就是MyObject的物件。
�M量不要�@���,��造成retain cycle,改善的方法是把建��子改成�@��
-(id) initWithTitle:(NSString * ) newTitle{
if(self = [super init]){
title = newTitle;
myLog = [^(NSString * deco) {NSLog(@" %@%@%@",deco, newTitle, deco ); } copy];
}
return self;
}
在Block主�w�e用newTitle�@����刀�不是title。�@��self就不��被retain了。
最後�一��小陷井
void (^myLog) (void);
BOOL result ;
if(result)
myLog = ^ {NSLog(@"YES");};
else
myLog = ^ {NSLog(@"NO");};
myLog();
�@�雍芸赡芫����掉了,因��myLog ���w在if 或是else�Y束後就被清掉了。要�得。
要用copy�斫�Q�@�����},但要�得release。
//以下是个人见解:Block在IOS开发中使用还是比较频繁的,以下是我总结的一个例子:
//目标:使用Block函数实现对数组对象的遍历,并在匹配到指定字符串停止遍历
//NSArray类扩展 #import <Foundation/Foundation.h> typedef void (^MyBlock)(NSString *str,BOOL *stop); @interface NSArray (Bolck) - (void)BlockTest:(MyBlock)enumerationBlock; @end //NSArray类扩展方法体实现 #import "NSArray+Bolck.h" @implementation NSArray (Bolck) //使用块读取 - (void)BlockTest:(MyBlock)enumerationBlock { BOOL mybool=YES; for (NSString * temp in self) { enumerationBlock(temp,&mybool); if (mybool == NO)//回传值,这样极大的方便了开发者封装和灵活性 { break; } } } @end //main方法体 #import <Foundation/Foundation.h> #import "NSArray+Bolck.h" #import <Foundation/Foundation.h> #import "NSArray+Bolck.h" int main(int argc, const char * argv[]) { @autoreleasepool { NSArray * array = [[NSArray alloc]initWithObjects:@"One",@"Two",@"Three",@"Four",@"Five",nil]; [array BlockTest:^(NSString *str,BOOL *stop) { NSLog(@"%@",str); if ([str isEqualToString:@"Three"]) { *stop = NO; } }]; return 0; } } 运行结果: 2014-11-18 21:06:02.008 Block[4364:207213] One 2014-11-18 21:06:02.009 Block[4364:207213] Two 2014-11-18 21:06:02.010 Block[4364:207213] Three Program ended with exit code: 0
总结:在IOS开发中,一般Block的使用主要就是为了对象调用函数时,看该函数内部是否满足某种条件,若满足则将执行结果当做参数传递给Block函数,用来调用该Block方法体!以下是一个IOS使用Block方法的函数:
typedef void (^ALAssetsLibraryGroupsEnumerationResultsBlock)(ALAssetsGroup *group, BOOL *stop); typedef void (^ALAssetsLibraryAccessFailureBlock)(NSError *error); - (void)enumerateGroupsWithTypes:(ALAssetsGroupType)types usingBlock:(ALAssetsLibraryGroupsEnumerationResultsBlock)enumerationBlock failureBlock:(ALAssetsLibraryAccessFailureBlock)failureBlock; 该方法体来源与函数库:这个函数的用途就是枚举手机内的所有相册,每枚举到一个就会调用ALAssetsLibraryGroupsEnumerationResultsBlock块函数,这极大的方便开发者在函数块满足条件时,实现自己想要实现的代码,增加灵活性!
转载自:http://fei263.blog.163.com/blog/static/9279372420113193523828/