iOS 中block結構的簡單用法

自從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

相關文章
相關標籤/搜索