前言
block
是一种比较特殊的数据类型。它可以保存一段代码,在合适的时候取出来调用。
block的声明
- 方式一
typedef int(^myBlock)(int, int);
@interface Person : NSObject
@property (nonatomic, copy) myBlock myBlock;
@end
- 方式二
@property (nonatomic, copy) void (^callback)(NSString *name);
- 方式三
- (void)func:(int (^)(int, int))callback;
block的实现
self.carson.callback = ^NSString *(NSString *name) {
NSLog(@"%@", name);
};
block的作用域
block
需要注意的一个特性就是Variable Capturing
,直译过来就是捕捉变量。block会将“捕捉”到的变量复制一份,然后对复制品进行操作。如果希望block
作用域内可以修改外边的变量,可以使用__block
__block int i = 0;
self.carson.callback = ^NSString *(NSString *name) {
NSLog(@"%@", name);
i ++;
};
somePerson.callback(@"张三");
NSLog(@"i == %d", i);
打印结果:
2018-02-02 15:53:13.386997+0800 Block[6754:448733] i == 1
block的三种类型
-
_NSConcreteGlobalBlock
(全局block)
全局块存储在静态区(也叫全局区),相当于Objective-C
中的单例。 -
_NSConcreteStackBlock
(栈块)
栈块存储在栈区,超出作用域则马上被销毁。 -
_NSConcreteMallocBlock
(堆块)
堆块存储在堆区中,是一个带引用计数的对象,需要自行管理其内存。
怎么判断一个block
所在的存储位置呢?
-
block
不访问外界变量(包括栈中和堆中的变量)此时就为全局块 - 访问外界变量的
block
默认存放在堆中,实际上是先放在栈区,在ARC
情况下自动又拷贝到堆区,自动释放。如果不是ARC
则存放在栈区,。 - 使用
copy
修饰符的作用就是将block
从栈区拷贝到堆区
声明block属性的时候为什么用copy
使用copy
修饰符的作用就是将block
从栈区拷贝到堆区,复制到堆区的主要目的就是保存block
的状态,延长其生命周期。因为block
如果在栈上的话,其所属的变量作用域结束,该block
就被释放掉。
不同类型的block
使用copy
方法的效果也不一样,如下所示:
![](https://img.haomeiwen.com/i3221470/cf3ca57edfe7766f.png)
block的循环引用
在使用block
的时候,我们要特别注意循环引用的问题,先来看一个循环引用的例子:
@interface Person : NSObject
@property (nonatomic, copy) void (^callback)(NSString *name);
@property (nonatomic, assign) NSInteger age;
@end
@interface ViewController ()
@property (nonatomic, strong) Person *carson;
@end
self.carson.callback = ^void (NSString *name) {
self.carson.age = 5;
};
在上面的代码中Person
类声明了一个block
属性,所以Person
对象carson
对block
有一个强引用。而在block
内部又对carson
进行了一次强引用,这样就形成了一个封闭的环,也就是我们经常说的强引用循环。由于其相互引用,内存不能够进行释放,就造成了内存泄漏的问题。
__weak解决循环引用
__weak typeof(self) weakSelf = self;
self.carson.callback = ^void (NSString *name) {
weakSelf.carson.age = 5;
};
但是在并发执行的时候,block
的执行是可以抢占的,而且对weakSelf
指针的调用时序不同可以导致不同的结果,比如在一个特定的时序下weakSelf
可能会变成nil,这个时候在执行doAnotherThing
就会造成程序的崩溃。为了避免出现这样的问题,采用__strong
的方式来进行避免,更改后的代码如下:
__weak typeof(self) weakSelf = self;
self.carson.callback = ^void *(NSString *name) {
__strong typeof(weakSelf) strongSelf = weakSelf;
strongSelf.carson.age = 5;
};
总结:
-
self
没有直接或间接强引用block
,可以在block
里面直接使用self
,比如UIView
的animation
动画block
。 -
self
强引用对象方法里的block
也可以直接使用self
,因为没有强引用到block
,所以不会造成循环引用。 - 当和并发执行相关的时候,当涉及异步的服务的时候,
block
可以在之后被执行,并且不会发生关于self
是否存在的问题。
网友评论