小码哥iOS学习笔记第十七天: Runloop线程保活

一、线程失活

  • 新建OC工程, 定义BWThread继承自NSThread, 重写-dealloc方法如下图

  • Main.storyboard中结构如下

  • ViewController中代码如下

  • 运行程序, pushViewController中, 有如下打印

  • 当创建的子线程执行完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失活后再次popViewController, 就会发生运行时错误

  • 这主要是因为waitUntilDone设置为YES, 程序会等thread执行完之后再执行后面的代码, 此时thread已经失活, 所以才会出现运行时错误
  • 我们可以再停止thread之后, 将self.thread置空, 再加入下面的判断, 就可保证程序的正常运行

  • 运行程序, 进入ViewController后, 先点击释放按钮, 使thread失活后再次popViewController, 可以看到程序正常运行

  • 其中ViewControllerthread也都被释放了

转载于:https://juejin.im/post/5c8cba495188257e16048237

你可能感兴趣的:(小码哥iOS学习笔记第十七天: Runloop线程保活)