一、线程失活
- 新建OC工程, 定义
BWThread继承自NSThread, 重写-dealloc方法如下图
Main.storyboard中结构如下
ViewController中代码如下
- 运行程序,
push到ViewController中, 有如下打印
- 当创建的子线程执行完
block后, 会立即释放掉
二、Runloop线程保活
- 每一条线程都有与之相对应的唯一一个
RunLoop, 只有在主动获取RunLoop时才会创建(主线程中的RunLoop由系统自动创建) - 我们可以通过在线程中使用
RunLoop来对线程保活 - 当然, 我们在使用
RunLoop对线程进行保活的时候, 不能仅仅运行就行了, 因为RunLoop当前执行的_currentModel中如果没有Sources0, Sources1, Timers, Observers, 那么RunLoop会自动退出
- 所以我们需要创建一个事件让
RunLoop处理, 这样RunLoop才不会退出 - 运行程序,
thread留在了内存中, 没有被释放
- 我们可以引用
thread, 然后在touchesBegan:withEvent:方法中,给子线程添加事件
- 运行后点击屏幕, 可以看到
thread保持活性, 依然在可以事件
- 如果去掉
RunLoop, 可以看到,不论怎么点击屏幕, 都不会再有事件, 此时thread就会失活, 所以就算被ViewController强引用, 依然无法保持thread的活性
三、释放RunLoop保活的线程
- 在
ViewController重写-dealloc方法, 可以发现当ViewController退出被销毁时,thread依然留在了内存中, 没有被释放, 说明thread发生了内存泄漏
-
我们可以通过停止
RunLoop的运行, 来释放thread -
在
ViewController上添加一个按钮, 我们要在点击释放按钮时, 将RunLoop停止
ViewController中代码如下, 当点击释放按钮时, 在thread中停止RunLoop
- 运行程序, 点击
释放按钮, 可以发现RunLoop并没有被停止, 点击屏幕依然触发事件
- 我们可以查看
RunLoop的-run方法的介绍
- 可以看到
-run方法里面, 实际上是一个死循环, 在不停的调用-runMode:beforeDate:方法 - 而我们通过
CFRunLoopStop(CFRunLoopGetCurrent())释放掉的, 只不过是其中某一次循环中的-runMode:beforeDate:而已 - 所以我们在调用
RunLoop的时候, 需要使用-runMode:beforeDate:方法, 而不是-run方法
- 运行程序, 可以看到, 当点击屏幕后,
thread在执行一次事件之后就会失活, 所以我们需要对-runMode:beforeDate:进行循环处理
- 再次运行程序, 可以看到点击屏幕后可以连续的响应事件, 只不过
释放之后RunLoop依然在工作
-
这是因为
-runMode:beforeDate:被停止后, 通过循环又进行了一次-runMode:beforeDate: -
所以, 我们需要使用一个标识来控制是否循环
-runMode:beforeDate:
- 再次运行程序, 就可以控制
thread的存活了
- 将
ViewController销毁, 可以看到thread也销毁了
设置thread随着ViewController释放一起释放
- 运行程序, 进入
ViewController后直接退出, 可以发现ViewController被释放了, 而thread并没有释放
- 在
-dealloc中执行-freeThread:方法, 执行程序
- 根据打印的信息, 可以看到执行了
stop方法后,thread依然没有被释放 - 这主要是因为, 在
ViewController的-dealloc方法执行时,ViewController已经处于被释放的状态, 当需要执行到stop时,ViewController已经被释放 - 所以, 我们需要设置当
thread的任务-stop执行完之后, 在执行后面的代码
- 接着我们执行程序, 可以发现,
-stop执行完之后才执行的-dealloc, 说明-stop执行完之后,ViewController才被释放 - 只是此时
thread依然没有被释放
- 这是因为, 等到
RunLoop被停止, 再次进入while循环判断的时候ViewController已经被释放, 此时的weakSelf的值为nil, 所以!weakSelf.isStop的值为YES, 再次进入了循环, 启动了RunLoop
- 所以, 在开启RunLoop的循环条件中, 加入
weakSelf必须有值的条件语句
- 这样, 就可以在
ViewController退出时,thread也跟着正常释放
解决: 点击释放后退出ViewController时崩溃的问题
- 当进入
ViewController后, 先点击释放按钮, 使thread失活后再次pop掉ViewController, 就会发生运行时错误
- 这主要是因为
waitUntilDone设置为YES, 程序会等thread执行完之后再执行后面的代码, 此时thread已经失活, 所以才会出现运行时错误 - 我们可以再停止
thread之后, 将self.thread置空, 再加入下面的判断, 就可保证程序的正常运行
- 运行程序, 进入
ViewController后, 先点击释放按钮, 使thread失活后再次pop掉ViewController, 可以看到程序正常运行
- 其中
ViewController和thread也都被释放了