一、block的基本概念和語法 html
基本概念:ios
block叫代碼塊、閉包或者匿名函數。包含返回值類型、block標記、名稱、參數和主體,相似C語言的函數。它本質上是可移植的匿名函數,可做爲方法和函數的參數傳入,或可從方法和函數中返回。git
語法結構:github
返回類型(^名稱)(參數...) = ^(參數) { 主體... };算法
/*例子:這是一個返回值類型爲 int 名稱爲fristBlock 帶有一個int類型參數的block。*/ int (^fristBlock)(int) = ^(int a) { int b = a + 200; return b; };
Apple所推薦的block使用範圍包括如下幾個方面:多線程
排序 ——在block內寫排序算法閉包
View動畫 ——簡單明瞭的方式規定動畫app
完成處理 ——當方法執行完畢後執行block代碼函數
枚舉 ——經過block獲取枚舉對象或控制枚舉進程測試
通知 ——當某事件發生後執行block內的代碼
錯誤處理 ——當錯誤發生時執行block代碼
GCD多線程 ——多線程控制
二、block的簡單使用
block也叫匿名函數,因此用法相似C語言函數。兩步:實現和調用
/*一、實現block*/ int b = 100; void (^myBlock)(int a) = ^(int a) { // block 主體 int c = a + b; NSLog(@"c = %d",c); }; /*二、調用block:block中的代碼必須調用才能起做用,相似c函數的調用*/ myBlock(200); // 調用block
三、block做爲參數使用
block做爲一個代碼塊能夠被用來做爲方法或者函數的參數。
*/ 調用方法*/ [self blockMethod:^(int a, int b) { NSLog(@"a + b = %d",a + b); }]; /*定義一個以block爲參數的方法*/ - (void)blockMethod:(void(^)(int a,int b)) block { block(100,200); // 調用 }
四、使用typedef定義block
因爲block的語法不是很簡潔,直接使用不是很方便。因此咱們使用時一般使用typedef關鍵字來定義block,達到使用的方便簡潔。
/*使用typedef定義一個沒有返回值帶有兩個int參數的block類型名稱爲TestBlock,後面就能夠直接使用TestBlock定義對應的block變量了*/ typedef void(^TestBlock)(int a, int b); /*1.定義一個類型爲TestBlock的block。*/ TestBlock tBlock = ^(int a, int b){ NSLog(@"a + b = %d",a + b); }; tBlock(200,300); /*2.使用TestBlock類型的block做爲參數*/ - (void)blockMethodTypedef:(TestBlock)block { if(block) { block(100,200); } } /*3.使用TestBlock類型的block做爲返回值*/ - (TestBlock)returnBlockMethodTypedef { return ^(int a, int b){ NSLog(@"output two number %d and %d",a, b); }; }
五、block做爲屬性使用
block代碼塊能夠做爲類的屬性使用,使用copy關鍵字修飾
/*>> .h文件中定義*/ typedef int(^SumBlock)(int a, int b); @property(nonatomic, copy)SumBlock block; /*>> .m文件中實現\調用*/ /*一、實現block*/ self.block = ^(int a,int b) { return a + b; }; /*二、調用block,調用時先判斷block不爲nil再調用。由於block有可能沒實現*/ if (self.block) { int sum = self.block(100,200) NSLog(@"sum = %d",sum ); }
六、block訪問使用局部變量
block內部能夠使用外部的局部變量,可是不能修改。若是要想修改得使用__block關鍵字修飾變量。
/*例1. 訪問外部變量 */ int num = 100; int(^numBlock)(int) = ^(int a){ int result = num + a; // num += 100; 錯誤:block內只能訪問使用外部普通變量,可是不能修改。 return result; }; num = 0; NSLog(@"num = %d",num); // num = 0 NSLog(@"numBlock = %d",numBlock(200)); // numBlock = 300 /*例2. 修改外部變量*/ __block int var = 100; // block變量 int(^varBlock)(int) = ^(int a){ var += a; // __block標記的變量block內部能夠修改 return var; }; var = 0; NSLog(@"var = %d",var); // var = 0 NSLog(@"varBlock = %d",varBlock(200)); // varBlock = 200
如上打印結果,numBlock = 300, varBlock = 200;
解析:
block使用外部普通局部變量時是將變量的值拷貝一份到block中,也就是說block內的num再也不是原來同一個num了,因此後面修改num的值不會影響block中num。
使用__block修飾的變量在block中使用時不會進行拷貝,使用的是同一個。因此後面修改var的值會影響block中的var。
五、block內存管理
block的類別:三種類型的block
NSGlobalBlock 全局區的block,沒有使用任何外部變量的block
NSStackBlock 棧區的block,使用了外部局部變量的block
NSMallocBlock 堆區block,使用copy或Block_copy()拷貝後block就保持到了堆區中,須要管理內存。
block的拷貝:(MRC下)
使用copy或Block_copy()是將棧區的block拷貝到堆區中,引用計數不會+1。
block被拷貝後會對使用到的外部局部變量對象引用計數+1,可是全局變量、實例變量、靜態變量、__block變量引用計數不會+1。
block內使用了實例變量不會對實例引用計數+1,可是會對實例變量持有者應用計數+1。
MRC下須要手動使用copy或者Block_copy()將block拷貝到堆區中,而後內存本身管理,使用Block_release()對應引用計數-1。
ARC下不須要手動copy,當block使用了局部變量後會自動拷貝到堆區中,也就是說ARC下不須要手動對block進行copy和release。
例1:block內使用的對象的引用計數。
static NSObject *staticObj = nil; _instanceObj = [[NSObject alloc] init]; // 實例變量 globalObj = [[NSObject alloc] init]; // 全局變量 staticObj = [[NSObject alloc] init]; // 靜態變量 NSObject *localObj = [[NSObject alloc] init]; // 局部變量 __block NSObject *blockObj = [[NSObject alloc] init]; // __block變量 self.block = ^{ NSLog(@"%@",[_instanceObj retainCount]); // 1 NSLog(@"%@",[globalObj retainCount]); // 1 NSLog(@"%@",[staticObj retainCount]); // 1 NSLog(@"%@",[localObj retainCount]); // 2 NSLog(@"%@",[blockObj retainCount]); // 1 }; if (self.block) { _block(); }
例2:MRC下對block的拷貝
int value = 100; void(^block)(int) = ^(int var){ NSLog(@"result:%d",value + var); }; NSLog(@"%@",block); //<__NSStackBlock__: 0x7fff51896988> NSLog(@"引用計數:%ld",[block retainCount]); // 1 /* 使用copy或者Block_copy()把棧區的block拷貝到堆區,引用計數不會改變。*/ void(^mallocBlock)(int) = Block_copy(block); //void(^mallocBlock)(int) = [block copy]; mallocBlock(200); NSLog(@"%@",mallocBlock); // <__NSMallocBlock__: 0x7fa0ca617d10> NSLog(@"引用計數:%ld",[mallocBlock retainCount]); // 1 Block_release(mallocBlock); //堆區的block須要管理內存。
六、block的循環引
描述:
當一個對象持有了block後,block內部又使用了這個對象或者對象的實例變量、實例方法,這樣會使block也持有這個對象,這樣相互持有就是循環引用,從而致使對象沒法釋放引發內存泄露。
解決:
要解決循環引用只須要不要相互持有就好了,也就是說只須要一方持有就能夠不讓block再持有當前這個對象就OK。
/*循環引用:self持有block,block又持有了self。*/ self.block = ^{ self.info = @"這裏會有循環引用"; // block內部使用了self得實例變量會retain self [self testMeethod]; }; /*1. MRC下解決循環引用:不要在block內部直接使用self,要使用__block修飾的self,這樣block內部就不會對self retain了。*/ __block typeof(self)blockSelf = self; self.block = ^{ blockSelf.info = @"這樣就沒有循環引用了"; [_blockSelf testMethod]; }; /*2. ARC下解決循環引用:使用__weak修飾self後,block就不會再持有self了。*/ __weak typeof(self)weakSelf = self; self.block = ^{ weakSelf.info = @"這樣就不會循環引用了"; [weakSelf testMethod]; }; /*實例方法*/ - (void)testMethod { NSLog(@"測試方法!"); }
七、block實踐使用Demo(傳值 、事件處理)
Demo地址:https://github.com/fuqinglin/block-
參考:
一、 http://blog.csdn.net/yin_xianwei/article/details/17998165