声明和使用Block
^
操作符有两个作用:
- 表示声明一个block变量,
- 可以用来标识一个block定义的开始。
int multiplier = 7;
int (^myBlock)(int) = ^(int num) {
return num * multiplier;
};
image
直接使用Block
有很多场景下,我们不需要声明block就可以直接使用。
可以直接书写block的定义作为一个参数传递给函数
[NetWorkTool testBlock:^{
}];
__block标记变量
block可以直接使用和它处于相同作用域下的变量(高级别的变量,比如全局变量不仅可以使用,还可以直接修改),但是只能使用,无法修改。
给变量加上__block标识后,就可以进行修改了。
Block 和 变量
block内的变量会有五种对待方式:
- 全局变量和同等作用域里的静态变量 可以进行访问 (可以进行读写)
- 传递给block的参数 可以进行访问 (可以进行读写)
- 同等作用域下,堆栈上的变量,被捕获作为常量变量 (只可以读取值)
- 上面一条的变量,如果加上__block,就可以进行访问 (可以进行读写)
- 块内部的变量,其行为和正常的局部变量一样
对象 和 block变量
(这个很关键,block造成的内存泄漏问题一般都是这个原理)
当block被拷贝的时候,block内的对象变量会被强引用
- 如果你通过引用去访问实例变量,self就会被block进行强引用
- 如果你通过值去访问一个实例变量,这个变量会被强引用
dispatch_async(queue, ^{
// instanceVariable is used by reference, a strong reference is made to self
doSomethingWithObject(instanceVariable);
});
id localVariable = instanceVariable;
dispatch_async(queue, ^{
/*
localVariable is used by value, a strong reference is made to localVariable
(and not to self).
*/
doSomethingWithObject(localVariable);
});
循环引用相关例子
问题1: 下面这个例子,vc离开的时候能否正常被释放?
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
void(^MyTwoBlock)(void) = ^{
[self view];
};
MyTwoBlock();
}
回答:可以正常释放。 因为MyTwoBlock虽然会对self进行强引用,但是viewDidload执行完毕后,block就释放掉了,所以没有引用环。
问题2: 下面的例子,vc离开的时候能否正常被释放?
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
void(^MyTwoBlock)(void) = ^{
[self view];
};
self.kmjBlock = MyTwoBlock;
}
回答:会造成内存泄漏。因为self对MyTwoBlock有个强引用了。
问题3:属性设置成copy和strong有什么区别吗?
@property (nonatomic, strong) void(^kmjBlock)(void);//
@property (nonatomic, copy) void(^kmjBlock)(void);//
回答:没有区别。因为block是否在堆上是由系统直接完成的。如果一个block内部访问了外部的变量,就会自动被拷贝到堆上。所以这里用strong修饰和copy修饰,其实都只是增加了一个强引用。
block如何实现可以捕获外部变量以及可以在其他时机进行调用的?
大致原理是:系统先创建了一个block相关的结构体,将当前作用域下的变量(个人觉得只有局部变量需要传递)和block块的函数地址传递给这个结构体存下来,这样当block代码块要执行的时候,拿着结构体里存的信息就可以执行。
具体实现代码可以看这里:ios - block原理解读(一)
网友评论