Objective-C中 Block 在iOS在4.0版本的使用ios
瞭解何謂block。數組
瞭解block的使用方法。網絡
Block 是iOS在4.0版本以後新增的程序語法.多線程
在iOS SDK 4.0以後,Block幾乎出如今全部新版的API之中,換句話說,若是不瞭解Block這個概念就沒法使用SDK 4.0版本之後的新功能,所以雖然Block自己的語法有點難度,但爲了使用iOS的新功能咱們仍是得硬着頭皮去了解這個新的程序概念。異步
1、看一看什麼是Block函數
咱們使用'^'運算符來聲明一個Block變量,並且在聲明完一個Block變量後要像聲明普通變量同樣,最後要加';'。編碼
接下來,咱們經過一個例子來聲明一個簡單的Block變量.url
int (^block)( int ) = NULL;
咱們一塊兒來看一下聲明Block變量的語法spa
數據返回值類型 (^變量名) (參數列表) = NULL;線程
接下來,咱們爲剛纔的block變量進行賦值.
block = ^(int m){ return m * m; };
而後咱們一塊兒來使用一次這個block變量
//經過使用block變量,計算整型常量10的平方,而且打印在控制器輸出NSLog(@"10的平方是:%d",block(10));
注意,到目前咱們應該有發現block變量的使用步驟,有相似於函數的步驟
首先都要聲明(聲明函數,聲明block變量);
而後都要進行實現(實現函數,爲block變量賦值實現過程);
最後都要進行調用才能實現具體功能
2、看一看如何直接使用block參數
數組排序
//聲明數組變量 NSMutableArray *mutableArray = [[NSMutableArray alloc] initWithObjects:@"5",@"2",@"3",@"9",@"7", nil]; //直接使用block進行數組升序排序 [mutableArray sortUsingComparator:^NSComparisonResult(id obj1, id obj2) { //將兩個參數轉換爲字符串的對象 NSString *value1 = (NSString *)obj1; NSString *value2 = (NSString *)obj2; //value1與value2兩個對象的比較結果直接返回 return [value1 compare:value2]; }]; //打印可變數組變量 NSLog(@"%@",mutableArray);
簡單的網絡異步請求
//聲明網絡地址對象 NSURL *url = [NSURL URLWithString:@"http://www.qq.com"]; //根據網絡地址對象,聲明網絡請求對象 NSURLRequest *request = [NSURLRequest requestWithURL:url]; //直接使用block變量完成連接成功後的數據返回功能 [NSURLConnection sendAsynchronousRequest:request queue:[NSOperationQueue mainQueue] completionHandler:^(NSURLResponse *response, NSData *data, NSError *connectionError) { //將二進制數據使用utf8編碼轉換成字符串對象 NSString *result = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; //打印連接完成後的結果 NSLog(@"%@",result); }];
能夠看出ios平臺中的不少功能都已經集成了Block語法的處理方法.
3、看一看深刻理解Block語法
在本節,主要要去介紹的就是使用__block修飾的變量可以完成的做用。
先來看一個例子。
//聲明一個局部整型變量 int intValue = 3; //聲明一個返回值爲int,一個int參數的block變量 int (^block)(int) = ^(int m){ return m * intValue; }; //調用block變量,5做爲參數以後的結果 NSLog(@"block(5) = %d",block(5));
在上面的例子中,咱們將intValue變量稱爲block執行過程當中的外部變量,在block執行過程當中能夠直接使用該外部變量。
再看一個例子。
//聲明一個局部整型變量 int intValue = 3; //聲明一個返回值爲int,一個int參數的block變量 int (^block)(int) = ^(int m){ intValue++; return m * intValue; }; //調用block變量,5做爲參數以後的結果 NSLog(@"block(5) = %d",block(5));
在上面的例子中,咱們編譯程序後發現編譯器會有紅色錯誤,錯誤提示爲
Variable is not assignable (missing __block type specifier)
爲何會出現不能被賦值的錯誤提示呢?
block 在實現時就會對它引用到的它所在方法中定義的棧變量進行一次只讀拷貝,而後在 block 塊內使用該只讀拷貝。
那爲了不上述錯誤,就要使用__block修飾符來修飾外部變量,用來通知編譯器該外部變量intValue與block中的intValue指的是同一起內存地址,而不須要內存拷貝。以下例
//將intValue局部整型變量使用__block修飾符進行修飾 __block int intValue = 3; //聲明一個返回值爲int,一個int參數的block變量 int (^block)(int) = ^(int m){ intValue++; return m * intValue; }; //調用block變量,5做爲參數以後的結果 NSLog(@"block(5) = %d",block(5));
4、使用Block要注意的內存問題
使用 weak–strong dance 技術來避免循環引用
舉例以下
//// ViewController.m//// Created by lewis.//#import "ViewController.h"@interface ViewController ()@end@implementation ViewController { id observer; }- (void)viewDidLoad { [super viewDidLoad]; //添加觀察者,觀察主題修改消息通知,而且在收到消息通知後,打印視圖控制器對象 observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"kThemeChangeNotification" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) { NSLog(@"%@",self); }]; }- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}//當視圖控制器對象銷燬時,移除觀察者- (void)dealloc { if (observer) { [[NSNotificationCenter defaultCenter] removeObserver:observer]; } }@end
在上面代碼中,咱們添加向通知中心註冊了一個觀察者,而後在 dealloc 時解除該註冊,一切看起來正常。但這裏有兩個問題:
在消息通知 block 中引用到了 self,在這裏 self 對象被 block retain,而 observer 又 retain 該 block的一份拷貝,通知中心又持有 observer。所以只要 observer 對象尚未被解除註冊,block 就會一直被通知中心持有,從而 self 就不會被釋放,其 dealloc 就不會被調用。而咱們卻又指望在 dealloc 中經過 removeObserver 來解除註冊以消除通知中心對 observer/block 的 retain。
同時,observer 是在 self 所在類中定義賦值,所以是被 self retain 的,這樣就造成了循環引用。
解決方式以下.
//// ViewController.m//// Created by lewis.//#import "ViewController.h"@interface ViewController ()@end@implementation ViewController { id observer; }- (void)viewDidLoad { [super viewDidLoad]; //先聲明一個weak弱對象 __weak ViewController *wSelf = self; //添加觀察者,觀察主題修改消息通知,而且在收到消息通知後,打印視圖控制器對象 observer = [[NSNotificationCenter defaultCenter] addObserverForName:@"kThemeChangeNotification" object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note){ //在block的執行過程當中,使用強對象對弱對象進行引用 ViewController *bSelf = wSelf; if (bSelf) { NSLog(@"%@",bSelf); } }]; }- (void)didReceiveMemoryWarning { [super didReceiveMemoryWarning]; // Dispose of any resources that can be recreated.}//當視圖控制器對象銷燬時,移除觀察者- (void)dealloc { if (observer) { [[NSNotificationCenter defaultCenter] removeObserver:observer]; } }@end
首先,在 block 以前定義對 self 的一個弱引用 wSelf,由於是弱引用,因此當 self 被釋放時 wSelf 會變爲 nil;而後在 block 中引用該弱應用,考慮到多線程狀況,經過使用強引用 bSelf 來引用該弱引用,這時若是 self 不爲 nil 就會 retain self,以防止在後面的使用過程當中 self 被釋放;而後在以後的 block 塊中使用該強引用 sself,注意在使用前要對 bSelf 進行了 nil 檢測,由於多線程環境下在用弱引用 wSelf 對強引用 bSelf 賦值時,弱引用 wSelf 可能已經爲 nil 了。
經過這種weak-strong手法,block 就不會持有 self 的引用,從而打破了循環引用。