block的分类
block可分为三种
NSStackBlock:栈block
NSMallocBlock:堆block
NSGlobalBlock:全局block
1. 栈block
特点:生命周期由系统控制,函数返回即销毁
用到局部变量、成员属性\变量,且没有强指针引用的block都是栈block
a.用到局部变量,i为局部变量,block直接在NSLog中打印,没有被指针引用
- (void)viewDidLoad {
[super viewDidLoad];
int i = 10;
NSLog(@"%@",^{
NSLog(@"%d",i);
});
}
打印结果如下:
2017-02-21 19:05:02.417 blockDemo[11626:921093] <__NSStackBlock__: 0x7fff59b31a68>
b.用到成员属性\变量(图2),name为成员属性
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.name = @"lily";
NSLog(@"%@",^{
NSLog(@"%@",self.name);
});
}
@end
打印结果如下:
2017-02-21 19:08:07.825 blockDemo[11646:922905] <__NSStackBlock__: 0x7fff54283a68>
2. 堆block
特点:没有强指针引用即销毁,生命周期由程序员手动管理
栈block如果有强指针引用或copy修饰的成员属性引用就会被拷贝到堆中,变成堆block
a.强指针引用(图3),block被testBlock引用,testBlock就是一个block类型的强指针(ARC环境下默认就是强指针)
- (void)viewDidLoad {
[super viewDidLoad];
int i = 10;
void(^block)() = ^{
NSLog(@"%d",i);
};
NSLog(@"%@",block);
}
打印结果如下:
2017-02-21 19:11:25.929 blockDemo[11672:925232] <__NSMallocBlock__: 0x608000057730>
b.copy修饰的成员属性引用
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,copy) void(^testBlock)();
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
int i = 10;
self.testBlock = ^{
NSLog(@"%d",i);
};
NSLog(@"%@",self.testBlock);
}
- (void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
}
@end
打印结果如下:
2017-02-21 19:15:27.116 blockDemo[11699:928199] <__NSMallocBlock__: 0x6000000574f0>
3. 全局block
特点:命长,有多长?很长很长,人在塔在(应用程序在它就在)
没有用到外界变量,或者只用到全局变量、静态(static)变量的block就是全局block
a.没有用到外界变量,下图中block没有用到外界变量,所以就算用weak修饰也是全局block(举个例子而已,开发中不要用weak,用了也别说是笔者教的)
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,weak) void(^testBlock)();
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.testBlock = ^{
};
NSLog(@"%@",self.testBlock);
}
打印结果如下:
2017-02-21 19:21:20.174 blockDemo[11743:932777] <__NSGlobalBlock__: 0x10eeba0a0>
b.只用到全局变量、静态(static)变量,str为全局变量,str1为静态变量,只用到其中一个也是全局block
#import "ViewController.h"
@interface ViewController ()
@end
NSString *str = @"静态变量";
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
static NSString *str1 = @"静态变量";
void(^quanjvBlock)() = ^{
NSLog(@"%@,%@",str,str1);
};
NSLog(@"%@",quanjvBlock);
}
打印结果如下:
2017-02-21 19:27:37.323 blockDemo[11775:936994] <__NSGlobalBlock__: 0x1064f50a0>
分类总结
1.用到局部变量、成员属性 . 变量的block为栈block,生命周期系统控制,函数返回即销毁,
2.有强指针引用或copy修饰的成员属性引用的block会被复制一份到堆中成为堆block,没有强指针引用即销毁,生命周期由程序员控制
3.没有用到外界变量或只用到全局变量、静态变量的block为全局block,生命周期从创建到应用程序结束
block对外界变量的捕获
a.基本数据类型---局部变量
block会拷贝该变量的值当做常量使用,外界修改变量的值不会影响block内部,且block内部不能对其修改
block内部修改外界变量i的值直接报错,如果想要修改,可以在int i = 0前面加上关键字__block,此时i等效于全局变量或静态变量
- (void)viewDidLoad {
[super viewDidLoad];
int i = 10;
void(^block)() = ^{
NSLog(@"block内部的i值为 : %d",i);
};
i ++;
NSLog(@"i = %d",i);
block();
}
外界变量i从10变成了11,block内部打印依然是10
打印结果如下:
2017-02-21 19:47:38.682 blockDemo[11881:949181] i = 11
2017-02-21 19:47:38.683 blockDemo[11881:949181] block内部的i值为 : 10
b.基本数据类型---静态变量、全局变量、成员属性\变量
block直接访问变量地址,在block内部可以修改变量的值,并且外部变量被修改后,block内部也会跟着变
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,assign)int k;
@end
// 全局变量
int j = 10;
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 静态变量
static int i = 10;
self.k = 10;
void(^block)() = ^{
i ++;
_k ++;
j ++;
NSLog(@"block内部的i = %d,j = %d, k = %d",i,j,_k);
};
i ++;
_k ++;
j ++;
NSLog(@"i = %d,j = %d, k = %d",i,j,_k);
block();
}
block内部操作和外部操作都会影响到block里面的值
打印结果如下:
2017-02-21 20:03:58.726 blockDemo[11969:960622] i = 11,j = 11, k = 11
2017-02-21 20:03:58.727 blockDemo[11969:960622] block内部的i = 12,j = 12, k = 12
c.指针类型---局部变量
block会复制一份指针并强引用指针所指对象,且内部不能修改指针的指向
图中被注释掉的代码试图修改指针指向,所以会报错(如果想要修改,在前面加上__block),但是可以修改所指对象的值,如str从“abc”变成了“abcdef”
__block的作用:
我们都知道:Block不允许修改外部变量的值,这里所说的外部变量的值,指的是栈中指针的内存地址。__block 所起到的作用就是只要观察到该变量被 block 所持有,就将“外部变量”在栈中的内存地址放到了堆中。进而在block内部也可以修改外部变量的值 (详细介绍可以产考:http://www.jianshu.com/p/404ff9d3cd42)
- (void)viewDidLoad {
[super viewDidLoad];
NSMutableString *str = [[NSMutableString alloc]initWithString:@"abc"];
void(^block)() = ^{
// str = [[NSMutableString alloc]initWithString:@"def"];
[str appendString:@"def"];
NSLog(@"%@",str);
};
block();
}
打印结果如下 :
2017-02-21 20:34:17.318 blockDemo[12084:976636] abcdef
d.指针类型---全局变量、静态变量、成员变量\属性
block不会复制指针,但是会强引用该对象,内部可修改指针指向,block会强引用成员属性\变量所属的对象,这也是为什么block内部用到self.xxx或_xxx可能会引起循环引用的原因
图中str2为成员属性,由于NSString是不可变的,所以从打印结果可以看出,在block内部修改了外界指针变量的引用,指向了新的字符串
#import "ViewController.h"
@interface ViewController ()
@property(nonatomic,strong)NSString *str1;
@end
NSString *str2 = @"静态变量";
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
static NSString *str3 = @"静态变量";
self.str1 = @"属性";
void(^block)() = ^{
self.str1 = @"属性+";
str3 = @"静态变量+";
str2 = @"全局变量+";
NSLog(@"self.str1 = %@,str2 = %@,str3 = %@",self.str1,str2,str3);
};
block();
}
打印结果如下:
2017-02-21 20:50:25.486 blockDemo[12166:986285] self.str1 = 属性+,str2 = 全局变量+,str3 = 静态变量+