使用闭包回调的内存泄漏

时间:2017-08-18 10:12:45

标签: ios swift xcode memory-leaks

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

该服务使用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
    }
}

但是xcode上的内存图实际上显示了一个保留周期,服务和子服务内存都泄露了!!

https://www.elastic.co/guide/en/elasticsearch/reference/2.3/docs-update.html

此外,从图中可以看出,它表明闭包捕获的服务和子服务的实例很强,这当然会引起参考周期。这是令人惊讶的,因为我实际上将服务的弱引用传递给闭包。对我来说,似乎不是xcode向我展示的。

我做错了吗?

PS:如果我改变传递给子服务初始化器的闭包,这样内存泄漏就会消失。谁能解释一下呢?

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

1 个答案:

答案 0 :(得分:2)

在这个闭包调用表达式中:

{ [weak self] in self!.onSubserviceReady }()

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

然后,闭包由()调用,闭包评估self!.onSubserviceReady

如您所知,这是一个方法引用,它将该方法作为闭包返回。 (我们称之为 method-closure 。)

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

调用后,[weak self]不会影响评估结果。因此,如果您没有强烈引用闭包本身,[weak self]无效,只需将self设为可选。

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

{ [weak self] in self?.onSubserviceReady($0) }

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

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