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。