自從block出現以後,不少API都開始採用這樣的結構,因而可知,block確實有許多優點存在,這裏將一些簡單用法總結以下:dom
1、如何聲明一個block變量ide
咱們經過^符號來聲明block類型,形式以下:函數
void (^myBlock)();指針
其中第一個void是返回值,能夠是任意類型,中間括號中^後面的是這個block變量的名字,我把它命名爲myBlock,最後一個括號中是參數,若是多參數,能夠寫成以下樣式:code
int (^myBlock)(int,int);對象
一樣,你也能夠給參數起名字:內存
int (^myBlock)(int a,int b);作用域
不少時候,咱們須要將咱們聲明的block類型做爲函數的參數,也有兩種方式:get
一、-(void)func:(int (^)(int a,int b))block;編譯器
第二種方式是經過typedef定義一種新的類型,這也是大多數狀況下采用的方式:
二、typedef int (^myBlock)(int a,int b) ;
-(void)func:(myBlock)block ;
2、如何實現一個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和函數的功能很像。
注意:一、在上面的代碼裏 block1是一個對象,若是直接打印將打印對象地址
二、block(),加上後面的括號纔是執行block語句塊
3、block中訪問對象的微妙關係
一、若是你在一個block塊中僅僅訪問對象,而不是對他進行修改操做,是沒有任何問題的:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. 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內部將訪問的變量都備份了一份,若是咱們在內部修改,外部的變量並不會被修改,咱們能夠經過打印變量的地址來證實這一點:
- (void)viewDidLoad { [super viewDidLoad]; int tem=2; NSLog(@"%p",&tem); block1 = ^(int a,int b){ NSLog(@"%p",&tem); return tem+1; }; NSLog(@"%d",block1(1,1)); }
打印結果以下:
能夠看出,變量的地址已經改變。
二、__block 作了什麼
爲了能夠在block塊中訪問並修改外部變量,咱們常會把變量聲明成__block類型,經過上面的原理,能夠發現,其實這個關鍵字只作了一件事,若是在block中訪問沒有添加這個關鍵字的變量,會訪問到block本身拷貝的那一份變量,它是在block建立的時候建立的,而訪問加了這個關鍵字的變量,則會訪問這個變量的地址所對應的變量。咱們能夠經過代碼來證實:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, typically from a nib. int tem=2; block1 = ^(int a,int b){ return tem+a+b; }; tem=4; NSLog(@"%d",block1(1,1)); block1 = ^(int a,int b){ return tem+a+b; }; __block int tem2=2; tem2=4; NSLog(@"%d",block1(1,1)); }
結果:
三、一點點擴展
由此,咱們能夠理解,若是block中操做的對象是指針,那麼直接能夠進行修改,這包括OC對象,若是不是,則須要用__block關鍵字修飾。
四、關於引用計數
在block中訪問的對象,會默認retain:
UIImage * number; number = [[UIImage alloc]init] ; NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number)); block1 = ^(int a,int b){ NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number)); }; NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number));
結果以下:
而添加__block的對象不會被retain;
注意:若是咱們訪問類的成員變量,或者經過類方法來訪問對象,那麼這些對象不會被retain,而類對象會被return,最多見的時self:
typedef void(^myBlock)(int,int) ; @interface ViewController2 () { myBlock block1; __block UIImage * number; } @end @implementation ViewController2 -(void)dealloc{ NSLog(@"dealloc %@",self.class); NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number)); } - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor=[UIColor whiteColor]; number = [[UIImage alloc]init] ; NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number)); block1 = ^(int a,int b){ NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number)); }; //block1(1,1); NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number)); UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom]; btn.frame=CGRectMake(100, 100, 100, 100); btn.backgroundColor=[UIColor redColor]; [self.view addSubview:btn]; [btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside]; } -(void)click{ [self dismissViewControllerAnimated:YES completion:nil]; }
打印結果:
能夠看出,UIImage對象沒有被retain,而self也將循環引用,形成內存泄露。解決方法以下:
number = [[UIImage alloc]init] ; NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number)); UIImage * im = number; block1 = ^(int a,int b){ NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)im)); }; NSLog(@"%ld",CFGetRetainCount((__bridge CFTypeRef)number));
打印結果:
注意:根據這個機制,若是咱們將block用來傳值,在block不用時,務必要置爲nil,而在實現block的方法裏,務必要釋放;咱們經過代碼來解釋:
首先,建立三個ViewController,爲ViewController1,ViewController2,ViewController3;
一、在ViewController1中建立一個按鈕,跳轉ViewController2
二、在ViewController2中:
#import "ViewController2.h" #import "ViewController3.h" @interface ViewController2 () { UIButton * im; } @end @implementation ViewController3 -(void)dealloc{ NSLog(@"dealloc %@",self.class); } - (void)viewDidLoad { [super viewDidLoad]; UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom]; btn.frame=CGRectMake(300, 300, 100, 100); btn.backgroundColor=[UIColor redColor]; [btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:btn]; im = [[UIButton alloc]initWithFrame:CGRectMake(100, 100, 100, 100)]; im.backgroundColor=[UIColor blackColor]; [im addTarget:self action:@selector(rele) forControlEvents:UIControlEventTouchUpInside]; [self.view addSubview:im]; } -(void)rele{ [self dismissViewControllerAnimated:YES completion:nil]; } -(void)click{ ViewController3 * con = [[ViewController3 alloc]init]; [con setBlock:^{ im.backgroundColor=[UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1]; }]; [self presentViewController:con animated:YES completion:nil]; }
三、在ViewController3中:
#import "ViewController3.h" void (^myBlock)(); @implementation ViewController3 -(void)setBlock:(void(^)())block{ myBlock = [block copy]; } -(void)dealloc{ NSLog(@"dealloc %@",self.class); } - (void)viewDidLoad { [super viewDidLoad]; self.view.backgroundColor=[UIColor whiteColor]; myBlock(); UIButton * btn = [UIButton buttonWithType:UIButtonTypeCustom]; btn.frame=CGRectMake(100, 100, 100, 100); btn.backgroundColor=[UIColor redColor]; [self.view addSubview:btn]; [btn addTarget:self action:@selector(click) forControlEvents:UIControlEventTouchUpInside]; } -(void)click{ [self dismissViewControllerAnimated:YES completion:nil]; }
經過打印信息,咱們會發現,ViewController2不被釋放,緣由是其成員變量im被block中retain沒有釋放,咱們這樣作:
@interface ViewController2 () { UIButton * im; ViewController3 * tem; } -(void)rele{ [tem setBlock:nil]; [self dismissViewControllerAnimated:YES completion:nil]; } -(void)click{ ViewController3 * con = [[ViewController2 alloc]init]; tem=con; [con setBlock:^{ im.backgroundColor=[UIColor colorWithRed:arc4random()%255/255.0 green:arc4random()%255/255.0 blue:arc4random()%255/255.0 alpha:1]; }]; [self presentViewController:con animated:YES completion:nil]; }
這樣就解決了內存問題。
4、關於block的做用域
應避免將花括號中的block用於外面,若是須要,你能夠將這個block聲明爲全局的。
專一技術,熱愛生活,交流技術,也作朋友。
——琿少 QQ羣:203317592