iOS中block的使用詳解

block的聲明和簡(jiǎn)單使用

  • 蘋果官方文檔聲明,block是objc對(duì)象。
block的定義方式
  • 無返回值無參數(shù)的block
void(^block1)() = ^{
    NSLog(@"調(diào)用了block1");
};
  • 無返回值有參數(shù)的block,參數(shù)的類型和傳入?yún)?shù)的值不能省略,即int a不能省略
void(^block2)(int) = ^(int a){
       NSLog(@"調(diào)用了block2");     
};
  • 有返回值的block,返回值的類型可以省略,要是有參數(shù),參數(shù)的類型和傳入?yún)?shù)的值不能省略。
int(^block3)() = ^int{
    return 3;
};

block定義總結(jié)

總結(jié):block的定義,不管block有沒有返回值,block的實(shí)現(xiàn)部分都可以省略;若block有參數(shù),則參數(shù)類型和傳入?yún)?shù)的值不能省略。

  • 小tip:若初學(xué)者覺得block格式奇怪,記不住,可以輸入inline就可以出現(xiàn)block,如圖:
Paste_Image.png

block使用場(chǎng)景之----代理傳值

在開發(fā)中控制器逆?zhèn)鱾髦底畛S玫木褪怯么淼姆椒?,然而用block也可以實(shí)現(xiàn)逆?zhèn)鱾髦担腋雍?jiǎn)單方便。

  • 場(chǎng)景:假如在窗口的根控制器ViewController中點(diǎn)擊屏幕的view,模態(tài)彈出一個(gè)窗口ModalViewController,再點(diǎn)擊ModalViewController的view銷毀ModalViewController,同時(shí)將想要傳的值傳給ViewController。

  • 在ViewController中的- touchesBegan方法中,進(jìn)行到ModalViewController的跳轉(zhuǎn),同時(shí)給ModalViewController的block屬性聲明,注意!這里并沒有調(diào)用

- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    ModalViewController *modalVc = [[ModalViewController alloc] init];
    modalVc.view.backgroundColor = [UIColor brownColor];
    modalVc.block = ^(NSString *value) {
      
        NSLog(@"%@",value);
    };
    
    // 跳轉(zhuǎn)
    [self presentViewController:modalVc animated:YES completion:nil];
}
  • 在ModalViewController的.h文件中聲明一個(gè)block屬性,這個(gè)block屬性沒有返回值,接收一個(gè)字符串類型的參數(shù)
@property (nonatomic, strong) void(^block)(NSString *value);
  • 在ModalViewController的.m文件中,點(diǎn)擊控制器的view進(jìn)行block的調(diào)用,此時(shí)就會(huì)將字符串中的值傳給ViewController。注意if語句的判斷,否則block不存在的時(shí)候,會(huì)報(bào)壞內(nèi)存訪問錯(cuò)誤
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{
    if (_block) {
        _block(@"我是萌萌噠ModalViewController");
    }
    [self dismissViewControllerAnimated:YES completion:nil];
}
Paste_Image.png
block的內(nèi)存管理
  • block在MRC環(huán)境和ARC環(huán)境的內(nèi)存管理不同

MRC

  • 首先,在項(xiàng)目的building settings里面將環(huán)境改成MRC的環(huán)境
Paste_Image.png
  • block會(huì)根據(jù)其內(nèi)部引用的變量的類型不同而在不同的存儲(chǔ)空間
  • block可以存在全局區(qū),棧區(qū),堆區(qū)(__NSGlobalBlock__,__NSStackBlock__,__NSMallocBlock__)。
    • 若block中引用外部的局部變量,則打印結(jié)果是__NSStackBlock__,說明block存儲(chǔ)在棧區(qū)
- (void)viewDidLoad {
    [super viewDidLoad];
    //局部變量a
    int a = 3;
    void(^block)() = ^{
        NSLog(@"調(diào)用block %d",a);
    };
    NSLog(@"%@",block);
}
Paste_Image.png
  • 若block中的變量的全局變量后者靜態(tài)變量,則打印結(jié)果是__NSGlobalBlock__,說明block存儲(chǔ)在全局區(qū)
//全局變量a
int a = 3;

- (void)viewDidLoad {
    [super viewDidLoad];
    void(^block)() = ^{
        NSLog(@"調(diào)用block %d",a);
    };
    NSLog(@"%@",block);
}

- (void)viewDidLoad {
    [super viewDidLoad];
    //靜態(tài)變量a
    static int a = 3;
    void(^block)() = ^{
        NSLog(@"調(diào)用block %d",a);
    };
    NSLog(@"%@",block);
}
Paste_Image.png
  • 那么block什么時(shí)候保存在堆區(qū)呢?
  • 當(dāng)block屬性被copy修飾的時(shí)候放在堆區(qū),打印結(jié)果為__NSMallocBlock __
//定義copy修飾的block屬性
@property (nonatomic, copy) void(^block)();
- (void)viewDidLoad {
    [super viewDidLoad];
    int a = 3;
    void(^block)() = ^{
        NSLog(@"調(diào)用block%d",a);
    };
    self.block = block;
    NSLog(@"%@",self.block);
}
  • 假如我們嘗試用retain修飾block,會(huì)報(bào)這樣的警告,同時(shí)運(yùn)行的結(jié)果是保存在棧區(qū)。
Paste_Image.png
Paste_Image.png
總結(jié)
只要block沒有引用外部局部變量,block放在全局區(qū)
只要Block引用外部局部變量,block放在棧區(qū).
block只能使用copy,不能使用retain,使用retain,block還是在棧區(qū)

ARC

  • 若block中的變量的全局變量后者靜態(tài)變量,則打印結(jié)果仍然是__NSGlobalBlock__,block仍然存儲(chǔ)在全局區(qū)
  • 若block中引用外部的局部變量,則打印結(jié)果是__NSMallocBlock__,說明block存儲(chǔ)在堆區(qū),和MRC情況不同。
  • 對(duì)于ARC下的block屬性用strong和copy修飾都是一樣的,但是copy的底層會(huì)讓setter方法多一層對(duì)可變不可變屬性的判斷,浪費(fèi)資源,所以在ARC的情況下用strong修飾block,同理NSString屬性修飾也用strong。
block的內(nèi)存管理總結(jié)
 總結(jié):block里面引用全局變量或者靜態(tài)變量,放在全局區(qū)__NSGlobalBlock__
 
 *  MRC情況:
 *  block里面引用局部變量,存放在棧區(qū)__NSStackBlock__
 *  MRC中修飾block只能用copy,不能用retain,因?yàn)閞etain修飾block還在棧中,若block是局部變量,不能包住block的命,再次訪問會(huì)造成壞內(nèi)存訪問。
 
 *  ARC情況:
 *  block里面引用局部變量,存放在堆區(qū)__NSMallocBlock__
 *  block在ARC情況下用strong修飾
 *  對(duì)于NSString和block在ARC盡量用strong,因?yàn)閏opy底層的setter方法會(huì)對(duì)新對(duì)象做一次copy操作,還要判斷是不是不可變對(duì)象,浪費(fèi)性能
 */
block的循環(huán)引用
  • block會(huì)對(duì)內(nèi)部的所有的強(qiáng)指針變量都強(qiáng)引用一次,這就會(huì)造成block的循環(huán)引用,造成內(nèi)存泄露。下面看例子:

在ViewController中以modal的方式展示ModalViewController,在ModalViewController中調(diào)用dismiss方法銷毀控制器。則在dealloc方法中會(huì)打印ModalViewController被銷毀。

然而若定義一個(gè)block屬性,并在block的實(shí)現(xiàn)中做如下操作:

@property (nonatomic, strong) void(^block)();
_block = ^{
    NSLog(@"%@",self);
};

此時(shí)當(dāng)調(diào)用ModalViewController的dismiss方法的時(shí)候不會(huì)調(diào)用dealloc方法中的打印語句,說明ModalViewController沒有被真正的銷毀。因?yàn)镸odalViewController強(qiáng)引用一個(gè)block屬性,block會(huì)對(duì)內(nèi)部的強(qiáng)指針self進(jìn)行一次強(qiáng)引用。所以造成循環(huán)引用

  • 改進(jìn)方法是將self改成弱指針,這樣就不會(huì)造成循環(huán)引用
__weak typeof(self) weakSelf = self;
_block = ^{
    NSLog(@"%@",weakSelf);   
};

block的變量傳遞

  • 請(qǐng)看這樣一段代碼,大家猜一猜打印的結(jié)果是3還是5?
/***************  1  **************/
- (void)viewDidLoad {
    [super viewDidLoad];    
    //普通局部變量a
    int a = 3;
    void(^block)() = ^{
        NSLog(@"%d",a);  
    };
    a = 5;
    block();
}

如果這樣呢?

/***************  2  **************/
- (void)viewDidLoad {
    [super viewDidLoad];    
    //靜態(tài)變量a
    static int a = 3;
    void(^block)() = ^{
        NSLog(@"%d",a);  
    };
    a = 5;
    block();
}

這樣呢?

/***************  3  **************/
//全局變量a
int a = 3;
- (void)viewDidLoad {
    [super viewDidLoad];    
    void(^block)() = ^{
        NSLog(@"%d",a);  
    };
    a = 5;
    block();
}

這樣呢?

/***************  4  **************/
- (void)viewDidLoad {
    [super viewDidLoad];    
    //用__block修飾的局部變量a
    __block int a = 3;
    void(^block)() = ^{
        NSLog(@"%d",a);  
    };
    a = 5;
    block();
}

實(shí)際編譯可以得出結(jié)果,除了1的打印結(jié)果是3,剩下的都是5。那么可以總結(jié)出結(jié)論如下:

* 如果block中是普通的局部變量,是值傳遞,即在局部變量聲明的那一刻就把局部變量的值放到block中了,之后在修改不會(huì)造成影響
* 如果是靜態(tài)變量,全局變量,__block修飾的變量,block都是指針傳遞。指針傳遞的值可以修改。
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 《Objective-C高級(jí)編程》這本書就講了三個(gè)東西:自動(dòng)引用計(jì)數(shù)、block、GCD,偏向于從原理上對(duì)這些內(nèi)容...
    WeiHing閱讀 10,118評(píng)論 10 69
  • 前言 Blocks是C語言的擴(kuò)充功能,而Apple 在OS X Snow Leopard 和 iOS 4中引入了這...
    小人不才閱讀 3,884評(píng)論 0 23
  • *面試心聲:其實(shí)這些題本人都沒怎么背,但是在上海 兩周半 面了大約10家 收到差不多3個(gè)offer,總結(jié)起來就是把...
    Dove_iOS閱讀 27,656評(píng)論 30 472
  • 目錄 Block概述 Block定義方式 Block保存代碼 Block傳值 Block對(duì)外部變量的傳遞 Bloc...
    子斌閱讀 1,510評(píng)論 2 7
  • __block和__weak修飾符的區(qū)別其實(shí)是挺明顯的:1.__block不管是ARC還是MRC模式下都可以使用,...
    LZM輪回閱讀 3,608評(píng)論 0 6

友情鏈接更多精彩內(nèi)容