自從block出現之後,很多API都開始采用這樣的結構,由此可見,block确實有許多優勢存在,這裡将一些簡單用法總結如下:
一、如何聲明一個block變量
我們通過^符号來聲明block類型,形式如下:
void (^myBlock)();
其中第一個void是傳回值,可以是任意類型,中間括号中^後面的是這個block變量的名字,我把它命名為myBlock,最後一個括号中是參數,如果多參數,可以寫成如下樣式:
int (^myBlock)(int,int);
同樣,你也可以給參數起名字:
int (^myBlock)(int a,int b);
很多時候,我們需要将我們聲明的block類型作為函數的參數,也有兩種方式:
1、-(void)func:(int (^)(int a,int b))block;
第二種方式是通過typedef定義一種新的類型,這也是大多數情況下采用的方式:
2、typedef int (^myBlock)(int a,int b) ;
-(void)func:(myBlock)block ;
二、如何實作一個block
既然block可以被聲明為變量,那麼就一定可以實作它,就像其他類型變量的指派。我自己對block的了解為它是一斷代碼塊,是以給它指派賦便是一段代碼段:
typedef int (^myBlock)(int,int) ;
@interface ViewController ()
{
myBlock block1;
}
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
block1 =^(int a, int b){
return a+b;
};
NSLog(@"%d",block1(1,1));
這裡列印的結果是2,從這裡可以發現block和函數的功能很像。
注意:1、在上面的代碼裡 block1是一個對象,如果直接列印将列印對象位址
2、block(),加上後面的括号才是執行block語句塊
三、block中通路對象的微妙關系
1、如果你在一個block塊中僅僅通路對象,而不是對他進行修改操作,是沒有任何問題的:
int tem=2;
block1 = ^(int a,int b){
int count= tem+1;
return count;
NSLog(@"%d",block1(1,1));
而如果我在block塊中直接修改,編譯器會報錯:
block1 = ^(int a,int b){
tem+=1;
return tem+1;
為什麼會出現這樣的情況,根據猜測,可能是block内部将通路的變量都備份了一份,如果我們在内部修改,外部的變量并不會被修改,我們可以通過列印變量的位址來證明這一點:
[super viewDidLoad];
NSLog(@"%p",&tem);
NSLog(@"%p",&tem);
NSLog(@"%d",block1(1,1));
列印結果如下:
可以看出,變量的位址已經改變。
2、__block 做了什麼
為了可以在block塊中通路并修改外部變量,我們常會把變量聲明成__block類型,通過上面的原理,可以發現,其實這個關鍵字隻做了一件事,如果在block中通路沒有添加這個關鍵字的變量,會通路到block自己拷貝的那一份變量,它是在block建立的時候建立的,而通路加了這個關鍵字的變量,則會通路這個變量的位址所對應的變量。我們可以通過代碼來證明:
block1 = ^(int a,int b){
return tem+a+b;
tem=4;
__block int tem2=2;
tem2=4;
NSLog(@"%d",block1(1,1));
結果: