您的位置:首页 > 移动开发 > IOS开发

iOS 什么情况下weak self需要配合strong self使用

2017-09-20 14:53 435 查看

前言

对于block和weak self这一对欢喜冤家,这篇博文默认你对此已经很熟悉了。

如果还不清楚block的几种形式,以及在什么情况下必须使用weak point什么情况下可以使用strong point,可以参考《block之三种blcok》 以及《block之循环引用》

综述

一般情况下我们使用__weak关键字定义一个强指针的弱引用;用__strong关键字定义一个弱指针的强引用,如下:

//__weak定义了一个指向self的弱指针weakS,当self释放之后weakS被置为nil
__weak  typeof(self) weakS = self;

//这里的strongS是个强指针,如果strongS初始化时self未释放,则在strongS被引用过程中self都会被保留而不会释放
__strong typeof(weakS) strongS = weakS;


使用weak关键字定义的指针不强引用对象,当变量释放后会自动置为nil,这一点和assign有所区别。

使用strong关键字定义的指针会强引用对象,当自身释放后,强引用对象才会释放。

weak 情景1 - 使用weak很安全

在使用block的过程中,我们经常见到的情况是这样:

//代码片段1
__weak  typeof(self) weak = self;
void (^block1)() = ^{
//为避免循环引用,在作为property的block中使用self的弱指针
NSLog(@" -- %@ --",weak);
};
_v.block1 = block1;


当然这样不会有什么问题,打印结果无非就两种,分别对应self释放与未释放,如下:

//self未释放
2017-09-20 13:52:21.025 BlockDemo[4237:149469]  -- <BVC: 0x7fab00905040> --

//self已经释放
2017-09-20 13:52:24.438 BlockDemo[4237:149469]  -- (null) --


weak 情景2 - weak并不总是安全

但是,你也可能遇到如下情况:

//代码片段2
__weak  typeof(self) weakS = self;
void (^block1)() = ^{
NSLog(@" -- %@ --",weakS);

//这里模拟一个dispatch_async,因为dispatch_after其实就是异步的
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@" == %@ ==",weakS);
});
};
_v.block1 = block1;


猜测一下会有几种打印结果呢?

答案是3种,带着疑惑请继续往下看:

1、两次打印self都没有释放
2017-09-20 13:47:33.090 BlockDemo[4141:145270]  -- <BVC: 0x7fde2d806940> --
2017-09-20 13:47:35.270 BlockDemo[4141:145270]  == <BVC: 0x7fde2d806940> ==

2、两次打印self都释放了
2017-09-20 13:49:09.955 BlockDemo[4141:145270]  -- (null) --
2017-09-20 13:49:12.154 BlockDemo[4141:145270]  == (null) ==

3、第一次打印self未释放,第二次打印self释放了
2017-09-20 13:50:02.526 BlockDemo[4190:147582]  -- <BVC: 0x7fcc98d02690> --
2017-09-20 13:50:04.526 BlockDemo[4190:147582]  == (null) ==


我们都知道,一般情况下(形如代码片段1)在block中使用weak self,如果block入栈时self未释放,则在block出栈前self是不可能被释放的,因此第一段代码只有两种可能性。

代码片段2就是另一种情形了,我来解释一下为什么会出现两次打印结果不一样的情况:

在block1入栈时self未释放,因此第一个NSLog()能打印出self的内存地址;

第二个NSLog()之所以打印出了(null),问题就在于block1出栈后,dispatch_after的代码块还未入栈,而self恰恰就在block1出栈后,而dispatch_after入栈前释放了,因此第二个NSLog()打印出了(null)。

dispatch_after明明就是写在block1中的,为什么block1出栈时,它还没入栈呢?

要想理解这个问题,首先要知道dispatch_after实际上是异步的,即它的实现实际上是基于dispatch_async的,执行时会立即返回,并不会卡主线程。

dispatch_async的作用就是将它的block任务块放进相应的队列中(队列其实就是任务列表),等待相关线程去执行,至于什么时候执行(函数入栈后才能执行)存在不确定性。

至此,你是否已经明白了为什么代码片段2中可能出现前后两次打印结果不一致的情况?

后续

如果已经对代码片段2出现的第三种打印情况有所了解,应该也就知道了本篇博文所讨论的问题答案了。

面对代码片段2的情况,我们只需要在block1中创建weakS的强引用即可保证block1和dispatch_after中的self相同(都未释放/都是nil)

__weak  typeof(self) weakS = self;
void (^block1)() = ^{
NSLog(@" -- %@ --",weakS);
__strong typeof(weakS) strongS = weakS;
//BVC * vc = weakS; 这种形式和__strong typeof(weakS)相同
//strongS其实只是个局部变量的强指针,它在作用域内不会被释放

//这里模拟一个dispatch_async,dispatch_after其实就是异步的
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
NSLog(@" == %@ ==",strongS);
});
};
_v.block1 = block1;


总结

总结一下strong self配合weak self使用的条件

block中用到了weak self

block中包含异步操作

异步操作中用到了self

当以上三个条件都成立的时候,为了避免block和异步操作中self(所有强指针)不一致,则应该在block中定义weak point的强引用,并在异步操作中使用。

//以下两种方式定义都是可以的,只是习惯上使用__strong
__strong typeof(weakS) strongS = weakS;
BVC * strongS = weakS;


如有不妥之处,欢迎指正,谢谢!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息