简体   繁体   中英

Why does wrapping a ReactiveSwift MutableProperty in a Property cause a leak?

Here's a Swift class that uses ReactiveSwift, wrapping a MutableProperty in a Property , and adding a subscription to that Property in a ScopedDisposable :

class Leaker {
    let mutableProperty = MutableProperty<Int>(0)

    var wrapperProperty: Property<Int> {
        return Property(self.mutableProperty)
    }

    private var disposable: ScopedDisposable<AnyDisposable>?

    init() {
        let disposable = CompositeDisposable()

        disposable += self.wrapperProperty.producer
            .startWithValues { value in
                print("\(value)")
            }

        self.disposable = ScopedDisposable(disposable)
    }
}

If I give another class a property of type Leaker? , and then set it using self.leaker = Leaker() , this creates a leak. By "creates a leak," I mean it sets off the Leaks instrument, showing a leaked object labeled Malloc 32 Bytes , with a stack trace that includes Leaker.init() calling Leaker.wrapperProperty.getter .

Why does this leak? I'm finding it hard to understand exactly what is causing the memory allocated here to never be released.

Some other facts that might be useful:

  • This doesn't leak if I subscribe to mutableProperty directly
  • This doesn't leak if I wrap mutableProperty in a lazy property instead of a computed property
  • This doesn't leak if I create a temporary Leaker , eg let _ = Leaker()

I tried, but was not able to reproduce the memory leak. I've used your exact Leaker class with this code:

final class OtherClass {
  let leaker: Leaker?

  init() {
    self.leaker = Leaker()
  }
}

var otherClass: OtherClass? = OtherClass()

var mutablePropertyCompleted = false
otherClass!.leaker!.mutableProperty.producer.startWithCompleted {
  mutablePropertyCompleted = true
}

var wrappedPropertyCompleted = false
otherClass!.leaker!.wrapperProperty.producer.startWithCompleted {
  wrappedPropertyCompleted = true
}

otherClass = nil

if(!mutablePropertyCompleted) {
  print("LEAK")
}

if(!wrappedPropertyCompleted) {
  print("LEAK")
}

One thing to note is that MutableProperty sends completed in its deinit method, so it should be possible to detect a leak the way I did here.

But just to be sure I also profiled this with the memory leaks instrument which detected nothing.

Since you mentioned some specific circumstances in which this does not leak for you it might just be that the way I'm trying to reproduce the issue is wrong, maybe you can post a complete example?

Anyway, I don't see an error in the example you posted, so either this is a bug in ReactiveSwift, or its a false positive by the profiler (which has happened with ReactiveSwift before as jjoelson pointed out in his comment)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM