概述
Block是苹果在iOS4开始引入的对C语言的扩展,用来实现匿名函数的特性,Block也是一种数据类型,它可以定义变量、做参数、作为返回值, Block是代码块,它可以保存一段代码,并且在需要的时候再调用.在iOS开发中,Block常被用于GCD、动画、排序、传值等的各类回调.
1.定义一个Block
typedef 返回值类型(^block名称)(参数列表);
//例如:
//无返回值无参数
void(^block)(void);
//无返回值有参数
void(^block)(NSString *params);
//有返回值有参数
int(^block)(NSString *params1,NSString *params2);//形参也可省略
2.赋值和调用
//声明
typedef void(^block)(NSString *params);
//属性
@property (nonatomic ,copy) block blockname;
//赋值
self.blockname = ^(NSString *params) {
NSLog(@"%@",params);
};
//调用
self.blockname(@"我是Block");
Block赋值只是保存了这样一段代码,只有我们调用Block之后他才会执行保存的这样一段代码,通常block的声明和调用在一个类,赋值是在另一个类,然后完成两个类之间的传值或者其他逻辑关系.
3.Block内部访问变量
-
1.Block访问外部变量的问题:
1.Block代码块内部可以声明一个与外部变量名称相同的变量,在Block内部访问时,遵循就近原则.
2.Block代码块内,可以取到全局变量和局部变量的值;
3.在Block代码块内可以修改全局变量的值,但是不能修改局部变量的值;
4.如果需要修改Block代码块外的局部变量的值,就要给这个局部变量使用__block修饰符.
5.__block的其实就是将局部变量, 拷贝到了堆区, 所以作用域结束, 实际上block修改的只是堆区的备份.为什么系统不给Block内部修改栈区局部变量
Block执行的时机并不仅仅是在局部变量生命周期的那段作用域中, 因为Block也是一个数据类型, 他可以被当做参数传递, 所以执行时, 可能局部变量已经不存在了, 因为栈区的数据, 在过其作用域就被销毁了.
4.block的存储位置
Block内部的代码决定了Block代码存在的区域
-
不访问外部变量:存储在全局区(执行的代码存储在代码段);
-
访问外部变量:
MRC下: 栈区 MAR+copy或ARC下:都存储在堆区(连执行的代码都存储在堆区,不存储在代码段)
block为什么用copy, 因为在MRC下, 不用copy, 拥有局部变量的代码, 存储在栈区, 当作用域结束, block也因为作用域的原因, 被弹栈了, 也就是被销毁了. 如果使用了copy, 那么block就被copy 到堆区了, 所以MRC下使用copy, 可以解决此问题, ARC下自动copy到堆区, 不使用copy也行, 所以也可以使用strong修饰.
5.block的循环引用
block作为属性时,被类强持有,在block其内部又使用持有它的类时会造成循环引用,内存泄露.
//造成循环引用
self.blockname = ^(NSString *params) {
NSLog(@"我是block-----%@",self);
};
//解决:
//1.ARC下使用 __weak
__weak typeof(self) weakSelf = self;
self.blockname = ^(NSString *params) {
NSLog(@"我是block-----%@",weakSelf);
};
//2.MRC下使用 __block
__block typeof(self) blockSelf = self;
self.someBlock = ^(NSString *params) {
NSLog(@"我是block-----%@",blockSelf);
};