使用闭包回调的ios内存泄漏

ont*_*oon 2 xcode memory-leaks ios swift

我有一个 Service 类,它保存一组子服务,其作用是使用 2 个闭包回调将一些事件发送回服务。

该服务使用 2 个闭包来初始化子服务对象,这两个闭包采用对服务的弱引用以避免保留周期。

public class T4fService {
    lazy var t4fSubServices: [T4fSubService?] = self.populateSubServices()
    let callbackOnServiceReady:  (_ isReady: Bool) -> ()
    let callbackOnBleEvent: (_ broadcastDict:  NotificationDict) -> ()

    init?(id: T4fServiceId,
      callbackOnServiceReady: @escaping (_ isReady: Bool) -> (),
      callbackOnBleEvent: @escaping CharacteristicCallback){

    self.callbackOnServiceReady = callbackOnServiceReady
    self.callbackOnBleEvent = callbackOnBleEvent
    }

func populateSubServices() -> [T4fSubService?]  {
    switch self.t4fServiceId {
    case .compassServiceId:
        return [T4fSubService( t4fBlePeripheralType: .Two4All, t4fCharacteristicUuidsArray: [T4fBleUuid.imuMagnetometerCharacteristicUuid],
                               callbackOnSubserviceReady: { [weak self] in self!.onSubserviceReady }(),
                               callbackOnBleEvent:  { [weak self] in  self!.onBleEvent}())]

func onSubserviceReady(_ isReady: Bool) {
    ...
    }
public func onBleEvent(broadcastDict: NotificationDict) {
    ...
    }
}

class T4fSubService{
    let callbackOnSubserviceReady:  (_ isReady: Bool) -> ()
    let callbackOnBleEvent: (_ broadcastDict:  NotificationDict) -> ()

    init?(t4fBlePeripheralType: T4fBlePeripheralType,
      callbackOnSubserviceReady: @escaping (_ isReady: Bool) -> (),
      callbackOnBleEvent: @escaping (_ broadcastDict:  NotificationDict) -> ()){
            self.callbackOnSubserviceReady = callbackOnSubserviceReady
    self.callbackOnBleEvent = callbackOnBleEvent
    }
}
Run Code Online (Sandbox Code Playgroud)

但是 xcode 上的内存图实际上显示了服务和子服务内存泄漏的保留周期!

在此输入图像描述

而且从图中可以看出,闭包捕获的Service和Subservice的实例很强大,这当然会导致引用循环。这是令人惊讶的,因为我实际上将服务的弱引用传递给了闭包。对我来说,xcode 向我展示的内容似乎并不真实。

我做错了什么吗?

PS:如果我以这种方式更改传递给子服务初始化程序的闭包,内存泄漏就会消失。谁能解释一下吗?

callbackOnSubserviceReady: { [weak self] in self?.onSubserviceReady($0) },
callbackOnBleEvent:  { [weak self]  in self?.onBleEvent(broadcastDict: $0)}
Run Code Online (Sandbox Code Playgroud)

OOP*_*Per 5

在此闭包调用表达式中:

{ [weak self] in self!.onSubserviceReady }()
Run Code Online (Sandbox Code Playgroud)

编译后的代码生成一个没有强引用的闭包self。

然后,闭包由 调用(),闭包计算self!.onSubserviceReady。

如您所知,这是一个方法引用,它以闭包的形式返回该方法。(我们称之为方法闭包。)

在 Swift 中,所有方法闭包都有对 的隐式强引用self,是否self为弱引用无关紧要。表达式self!.onSubserviceReady(或self.onSubserviceReady,当self为非可选时)始终返回相同的方法闭包,该方法闭包对 具有强引用self。

一旦调用,[weak self]不影响评估结果。因此,当您不对闭包本身进行强引用时,[weak self]没有任何效果,而只是设为self可选。


另一方面,你的闭包表达式:

{ [weak self] in self?.onSubserviceReady($0) }
Run Code Online (Sandbox Code Playgroud)

它本身就是一个闭包,并且您没有在那里调用闭包。因此,生成了一个新的闭包,它具有对 的弱引用self,并且该闭包(不是方法闭包!)被传递给初始值设定项并保存在实例属性中。


如果你想避免闭包引起的泄漏,你最好总是创建一个新的弱自闭包,而不是使用方法闭包。