基本使用
<returntype> (^blockname) (list of arguments) = ^(arguments) {body ;} ;
其中返回類型和參數(shù)可以省略
聲明
按照<returnValue> (^blockName) (parameters)
的方式進行block聲明未免麻煩了些厨内,可以通過關(guān)鍵字typedef
為block類型命名,然后直接通過類型名進行block創(chuàng)建渺贤。
- 命名
typedef <returntype> (^blockname)(list of arguments) ;
- 創(chuàng)建
blockname block = ^(arguments) {body ;} ;
__block關(guān)鍵字
CGPoint center = CGPointZero ;
CGPoint (^pointAddHandler)(CGPoint addPoint) = ^(CGPoint addPoint) {
return CGPointMake(center.x + addPoint.x, center.y + addPoint.y) ;
}
center = CGPointMake(100, 100) ;
NSLog(@"%@", pointAddHandler(CGPointMake(10, 10))) ; //輸出{10,10}
block在捕獲變量的時候會保存變量被捕獲時的狀態(tài)(對象變量除外)雏胃,之后即便變量再次改變,block中變量的值也不會發(fā)生改變志鞍。所以上述代碼在計算新的坐標值時center的值依舊為CGPointZero丑掺。如果希望在block中修改外界的本地變量,可以通過給這些變量加上__block
關(guān)鍵字來實現(xiàn)述雾。
循環(huán)引用
如果A創(chuàng)建并引用了B街州,B引用了callBackBlock兼丰,而callBackBlock中又引用了A,那么就會形成循環(huán)引用唆缴。解決方法是使用弱引用來解除這個循環(huán):__weak typeof(A) weakA = A ;
鳍征。但是如何理解block引起的循環(huán)引用問題呢?
創(chuàng)建一個BlockTestObject類面徽,它的兩個屬性如下:
@property (nonatomic, copy) NSString *str ;
@property (nonatomic, copy) void(^myBlock)() ;
測試代碼如下:
BlockTestObject *myTest = [[BlockTestObject alloc] init] ;
myTest.str = @"這是一個測試";
myTest.myBlock= ^{
NSLog(@"%@",myTest.str) ;
} ;
myTest.myBlock() ;
如果block代碼塊的內(nèi)部使用了外部的強引用對象艳丛,那么block代碼塊內(nèi)部就會自動生成一個強引用指向該對象。上述代碼中趟紊,myBlock會自動生成一個強引用指向myTest對象氮双,而myTest對象又有強引用指向myBlock,于是便造成了循環(huán)引用霎匈,使myTest對象無法被銷毀戴差。
解決這個問題常用方法就是使用 __weak
。
添加宏#define weakSelf(object) __weak typeof(object) weak##object = object ;
铛嘱,測試代碼修改為:
BlockTestObject *myTest = [[BlockTestObject alloc] init] ;
myTest.str = @"這是一個測試" ;
weakSelf(myTest) ;
myTest.myBlock = ^{
NSLog(@"%@",weakmyTest.str) ;
} ;
myTest.myBlock() ;
如果block代碼塊的內(nèi)部使用了外部的弱引用對象暖释,那么block代碼塊內(nèi)部就會自動生成一個弱引用指向該對象。上述代碼中墨吓,myBlock使用了弱引用對象weakmyTest球匕,因此myBlock只會生成一個弱引用指向?qū)ο髆yTest,從而不會造成循環(huán)引用帖烘。
由于僅有一個弱引用指向?qū)ο髆yTest亮曹,因此如果myBlock中的代碼出現(xiàn)延時執(zhí)行的情況,那么在該代碼執(zhí)行前對象myTest就有可能被銷毀秘症。測試代碼修改為:
BlockTestObject *myTest = [[BlockTestObject alloc] init] ;
myTest.str = @"這是一個測試" ;
weakSelf(myTest) ;
myTest.myBlock = ^{
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",weakmyTest.str) ;
}) ;
} ;
myTest.myBlock() ;
此時打印的結(jié)果為(null)
照卦,對象myTest在打印前就已經(jīng)銷毀±可以通過 __weak
和 __strong
一起使用來解決這個問題窄瘟。添加宏#define strongSelf(object) __strong typeof(object) object = weak##object ;
,測試代碼修改為:
BlockTestObject *myTest = [[BlockTestObject alloc] init] ;
myTest.str = @"這是一個測試" ;
weakSelf(myTest)
myTest.myBlock = ^{
strongSelf(myTest)
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2.0*NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",myTest.str) ;
}) ;
} ;
myTest.myBlock() ;
具體分析如下:
- 回調(diào)執(zhí)行
strongSelf(myTest)
這行代碼趟卸。一方面蹄葱,由于使用了外部的弱引用對象weakmyTest
,因此會自動生成一個弱引用指向?qū)ο髆yTest锄列。另一方面图云,block內(nèi)部定義的局部變量strongSelf(myTest)
會生成一個強引用指向?qū)ο髆yTest。 - GCD的
dispatch_after
代碼塊使用了該代碼塊外部的強引用對象myTest邻邮,因此會產(chǎn)生強引用指向?qū)ο髆yTest竣况。 -
dispatch_after
代碼塊會延遲2秒執(zhí)行,但是并不會阻塞線程筒严,因此myBlock會繼續(xù)執(zhí)行丹泉。當myBlock執(zhí)行完成時情萤,內(nèi)部的局部變量strongSelf(myTest)
就會銷毀,此時myBlock內(nèi)部指向?qū)ο髆yTest的強引用也會銷毀摹恨。 - 最后只剩下GCD的
dispatch_after
代碼塊有強引用指向?qū)ο髆yTest筋岛,所以對象myTest沒有被銷毀。當延時時間結(jié)束晒哄,dispatch_after
代碼塊執(zhí)行完成后就不會再有強引用指向?qū)ο髆yTest睁宰。 - 沒有強引用指向的對象myTest就會銷毀。