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,如圖:

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];
}

block的內(nèi)存管理
- block在MRC環(huán)境和ARC環(huán)境的內(nèi)存管理不同
MRC
- 首先,在項(xiàng)目的building settings里面將環(huán)境改成MRC的環(huán)境

- block會(huì)根據(jù)其內(nèi)部引用的變量的類型不同而在不同的存儲(chǔ)空間
- block可以存在全局區(qū),棧區(qū),堆區(qū)(
__NSGlobalBlock__,__NSStackBlock__,__NSMallocBlock__)。- 若block中引用外部的局部變量,則打印結(jié)果是
__NSStackBlock__,說明block存儲(chǔ)在棧區(qū)
- 若block中引用外部的局部變量,則打印結(jié)果是
- (void)viewDidLoad {
[super viewDidLoad];
//局部變量a
int a = 3;
void(^block)() = ^{
NSLog(@"調(diào)用block %d",a);
};
NSLog(@"%@",block);
}

- 若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);
}

- 那么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ū)。


總結(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都是指針傳遞。指針傳遞的值可以修改。