一榕吼、什么是Block?
Block是將函數(shù)及其執(zhí)行上下文封裝起來的對象。
比如:
NSIntegernum =3;
NSInteger(^block)(NSInteger) = ^NSInteger(NSIntegern){
????????return????n*num;? ?
?};? ? ? ??
block(2);
二、Block變量截獲
1奖年、局部變量截獲 是值截獲。
?比如:
NSIntegernum =3;
NSInteger(^block)(NSInteger) = ^NSInteger(NSIntegern){
? ??????return? ??n*num;? ??
};? ? ? ??
num =1;
NSLog(@"%zd",block(2));
這里的輸出是6而不是2沛贪,原因就是對局部變量num的截獲是值截獲陋守。
同樣震贵,在block里如果修改變量num,也是無效的水评,甚至編譯器會報錯猩系。
NSMutableArray* arr = [NSMutableArray????arrayWithObjects:@"1",@"2",nil];
void(^block)(void) = ^{
????????NSLog(@"%@",arr);//局部變量
????????[arr addObject:@"4"];? ??
};? ? ? ?
?[arr addObject:@"3"];? ? ? ?
?arr =nil;? ? ? ??
block();
打印為1,2中燥,3
局部對象變量也是一樣寇甸,截獲的是值,而不是指針疗涉,在外部將其置為nil拿霉,對block沒有影響,而該對象調(diào)用方法會影響
2咱扣、局部靜態(tài)變量截獲? 是指針截獲绽淘。
static????NSInteger????num =3;
NSInteger(^block)(NSInteger) = ^NSInteger(NSIntegern){
????????return????n*num;? ??
};? ? ? ?
?num =1;
NSLog(@"%zd",block(2));
輸出為2,意味著num = 1這里的修改num值是有效的闹伪,即是指針截獲沪铭。
同樣,在block里去修改變量m偏瓤,也是有效的杀怠。
三、Block的幾種形式
分為全局Block(_NSConcreteGlobalBlock)厅克、棧Block(_NSConcreteStackBlock)赔退、堆Block(_NSConcreteMallocBlock)三種形式
其中棧Block存儲在棧(stack)區(qū),堆Block存儲在堆(heap)區(qū)证舟,全局Block存儲在已初始化數(shù)據(jù)(.data)區(qū)
1离钝、不使用外部變量的block是全局block
比如:
NSLog(@"%@",[^{
????????NSLog(@"globalBlock");
}class]);
輸出:
__NSGlobalBlock__
2、使用外部變量并且未進行copy操作的block是棧block
比如:
NSInteger????num =10;
NSLog(@"%@",[^{
????????NSLog(@"stackBlock:%zd",num);? ??
}class]);
輸出:
__NSStackBlock__
日常開發(fā)常用于這種情況:
[self????testWithBlock:^{
????????NSLog(@"%@",self);
}];
- (void)testWithBlock:(dispatch_block_t)block {? ??
????????block();
????????NSLog(@"%@",[block????class]);
}
3褪储、對棧block進行copy操作,就是堆block慧域,而對全局block進行copy鲤竹,仍是全局block
比如堆1中的全局進行copy操作即賦值:
void(^globalBlock)(void) = ^{
????????NSLog(@"globalBlock");? ??
};
NSLog(@"%@",[globalBlock????class]);
輸出:
__NSGlobalBlock__
仍是全局block
而對2中的棧block進行賦值操作:
NSInteger????num =10;
void(^mallocBlock)(void) = ^{
????????NSLog(@"stackBlock:%zd",num);? ??
};
NSLog(@"%@",[mallocBlock????class]);
輸出:
__NSMallocBlock__
對棧blockcopy之后,并不代表著棧block就消失了昔榴,左邊的mallock是堆block辛藻,右邊被copy的仍是棧block
比如:
[self????testWithBlock:^{
NSLog(@"%@",self);
}];
- (void)testWithBlock:(dispatch_block_t)block{? ??
????????block();? ? ? ??
????????dispatch_block_t tempBlock = block;
????????NSLog(@"%@,%@",[block????class],[tempBlock????class]);
}
輸出:
__NSStackBlock__ ?, ? __NSMallocBlock__
即如果對棧Block進行copy,將會copy到堆區(qū)互订,對堆Block進行copy吱肌,將會增加引用計數(shù),對全局Block進行copy仰禽,因為是已經(jīng)初始化的氮墨,所以什么也不做纺蛆。
另外,__block變量在copy時规揪,由于__forwarding的存在桥氏,棧上的__forwarding指針會指向堆上的__forwarding變量,而堆上的__forwarding指針指向其自身猛铅,所以字支,如果對__block的修改,實際上是在修改堆上的__block變量奸忽。
即__forwarding指針存在的意義就是堕伪,無論在任何內(nèi)存位置,? 都可以順利地訪問同一個__block變量栗菜。
另外由于block捕獲的__block修飾的變量會去持有變量欠雌,那么如果用__block修飾self,且self持有block苛萎,并且block內(nèi)部使用到__block修飾的self時桨昙,就會造成多循環(huán)引用,即self持有block腌歉,block 持有__block變量蛙酪,而__block變量持有self,造成內(nèi)存泄漏翘盖。
比如:
__block????typeof(self) weakSelf =self;? ? ? ??
_testBlock = ^{
????????NSLog(@"%@",weakSelf);? ??
};? ? ? ?
?_testBlock();
如果要解決這種循環(huán)引用桂塞,可以主動斷開__block變量對self的持有,即在block內(nèi)部使用完weakself后馍驯,將其置為nil阁危,但這種方式有個問題,如果block一直不被調(diào)用汰瘫,那么循環(huán)引用將一直存在狂打。