block 的类型
在我们的日常开发过程中相信大家都会用到 block
,但是 block
有哪些类型,你又是否知道呢?下面我们来看一下 block
的类型区分。
block 三种类型
通过代码演示,我们可以看出,输入了三种类型的 block
,下面我们来介绍下这三种 block
类型:
-
GlobalBlock
- 位于全局区
- 在
block
内部不使用外部变量,或者只使用静态变量和全局变量
-
MallocBlock
- 位于堆区
- 在
block
内部使用变量或者oc
属性,并且赋值给强引用或者copy
修饰的变量
-
StackBlock
- 位于栈区
- 与
MallocBlock
一样,可以在内部使用局部变量或者oc
属性,但不能赋值给强引用或者copy
修饰的变量
block 拷贝到堆区的条件
- 手动
copy
-
block
作为返回值 - 被强引用或者
copy
修饰 - 系统
api
包含usingBlock
相关案例
-
案例一
这里我们自定义了一个 _CXBlock
类型的结构体,我们对 weakBlock
进行强转并赋值给 blc
,这样我们就可以对 block
内的数据结构进行修改,因为 weakBlock
, strongBlock
, strongBlock1
它们指向的是同一块内存区间,所有当我们把 blc
的 invoke
属性设置为 nil
后,strongBlock1()
的执行会报错,找不到函数执行。但是当我们把 id __strong strongBlock = weakBlock
改为 id __strong strongBlock = [weakBlock copy]
之后就可以解决这个问题,这是因为把栈 block
拷贝之后 blc
就为堆 block
。
-
案例二
因为 block
使用外部变量的时候会进行捕获,所以就会对引用计数加 1,第一次打印 3 是因为栈区 block
跟堆区 block
各引用了一次,打印 4 是因为这是一个栈区 block
,所以引用计数加 1,打印 5 是因为 weakBlock
拷贝之后变为了堆区 block
,又会对引用计数加 1。
-
案例三
这是一个关于 block
释放时机的问题,strongBlock
是栈区 block
,所以 strongBlock
的作用域是中间的大括号结束,所以 weakBlock = strongBlock
可以赋值成功,这时候 weakBlock
也是栈区 block
,所以它的作用域在最外层大括号结束的时候,所以 weakBlock()
可以执行成功。如果 strongBlock
是堆 block
, weakBlock()
就不会执行。
block 循环引用
对象的释放条件
循环引用条件
循环引用案例
// 循环引用
self.name = @"chenxi";
self.block = ^{
NSLog(@"%@",self.name);
};
类似这样一段代码,这种情况下就会出现循环引用,我们可以用 weakSelf
的方式来解决这个问题,代码如下:
- 解决循环引用方法一
self.name = @"chenxi";
__weak __typeof(self)weakSelf = self;
self.block = ^{
NSLog(@"%@",weakSelf.name);
};
虽然 weakSelf
的方式确实能解决循环引用问题,但是这种方式是不完善的,可以看如下案例:
类似这种,我们在打印的时候加个延时,这时候有种场景,当我们页面返回过快的时候,还没来得及打印,name
就已经释放了,所以打印为 null
。这时候解决这种问题我们需要用到 strongSelf
,如下:
self.name = @"xhenxi";
__weak __typeof(self)weakSelf = self;
self.block = ^{
__strong __typeof(weakSelf)strongSelf = weakSelf;
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@"%@",strongSelf.name);
});
};
self.block();
这里 strongSelf
只是一个临时变量,延长了 weakSelf
的释放时间,但是 strongSelf
作用域只是在 block
函数里面,block
函数执行完 strongSelf
就会释放。
- 解决循环引用方法二
- (void)viewDidLoad {
[super viewDidLoad];
self.name = @"xhenxi";
__block ViewController *vc = self;
self.block = ^{
NSLog(@"%@",vc.name);
vc = nil;
};
self.block();
}
这里用了一个临时变量 vc
接收 self
,在打印完毕后把 vc
置为 nil
。
- 解决循环引用方法三
typedef void(^CXBlock)(ViewController *vc);
@interface ViewController ()
@property (nonatomic, copy) CXBlock block;
@property (nonatomic, copy) NSString *name;
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
self.name = @"xhenxi";
self.block = ^(ViewController *vc) {
NSLog(@"%@",vc.name);
};
self.block(self);
}
这里是把 self
作为一个参数传递给 block
。
循环引用相关面试题
- 试题一
static ViewController *staticSelf_;
- (void)blockWeak_static {
__weak typeof(self) weakSelf = self;
staticSelf_ = weakSelf;
}
- (void)dealloc {
NSLog(@"dealloc 调用");
}
类似这样一段代码,我们把 weakSelf 赋值给了一个全局静态变量 staticSelf_,运行指挥,dealloc 方法并没有走,会导致不释放。这是因为 weakSelf 与 self 指向的是同一片内存空间,所以把 weakSelf 赋值给 staticSelf_ 之后会导致内存不释放。
- 试题二
typedef void(^CXBlock)(void);
@interface ViewController ()
@property (nonatomic, copy) CXBlock block;
@property (nonatomic, copy) CXBlock doWork;
@property (nonatomic, copy) CXBlock doStudent;
@end
- (void)block_weak_strong {
__weak typeof(self) weakSelf = self;
self.doWork = ^{
__strong typeof(self) strongSelf = weakSelf;
weakSelf.doStudent = ^{
NSLog(@"%@", strongSelf);
};
weakSelf.doStudent();
};
self.doWork();
}
- (void)dealloc{
NSLog(@"dealloc 调用");
}
这里也会出现循环引用问题,因为 doStudent
会对 strongSelf
进行捕获,所以 strongSelf
引用计数会加 1,所以出了 doWork
函数之后, strongSelf
会释放不掉。
网友评论