Swift Struct Memory Leak

≯℡__Kan透↙ 提交于 2019-12-31 08:13:27

问题


We're trying to use Swift structs where we can. We are also using RxSwift which has methods which take closures. When we have a struct that creates a closure that refers to self, that creates a strong reference cycle.

import Foundation
import RxSwift

struct DoesItLeak {

    var someState: String = "initial value"
    var someVariable: Variable<String> = Variable("some stuff")

    let bag = DisposeBag()

    mutating func someFoo() {

        someVariable.subscribeNext { person in

            self.someState = "something"
        }
        .addDisposableTo(bag)
    }
}

How do I know this? If I create 100,000 DoesItLeak objects and call someFoo() on each of them, I believe I have 100,000 objects with strong reference cycles. In other words, when I get rid of the DoesItLeak array containing those objects, the objects stay in memory. If I do not call someFoo(), there is no problem.

Variable is a class. So, I can see this memory issue by using xcode's Instruments' Allocations and filtering in Variable< String >

If I try to use [weak self] such as in the following, I get a compiler error:

someVariable.subscribeNext { [weak self] person in

The compiler error is "weak cannot be applied to non-class type"

In real/non-example code, we access methods and variables via self and it's a memory issue.

How can I resolve this memory issue while keeping the DoesItLeak a struct?

Thanks for your help.


回答1:


As Darren put it in the comments: "DoesItLeak can't be a struct" We cannot have the DoesItLeak be a struct and safely resolve the strong reference cycle issue.

Value types like structs exist on the stack frame. Closures and classes are reference types.

As the Strong Reference Cycles for Closures section puts it:

This strong reference cycle occurs because closures, like classes, are reference types.

Since the struct has the Variable class and the closure referring to self is stored into the Variable class using subscribeNext, it creates the strong reference cycle. See "Resolving Strong Reference Cycles for Closures" in Automatic Reference Counting Apple documentation.




回答2:


For anyone still facing this issue.

1) [weak self] is not possible because Struct is value type and not a Reference type, so no pointer as such.

2) The main issue of leak here is you are trying to access the Struct property self.someState = something inside the completion block which will basically create a new copy of your structure on assignment.

You should not access Struct Property inside completion block.




回答3:


The pattern of capturing self by an escaping closure in a writable context is now disallowed. The swift compiler will emit an error "Closure cannot implicitly capture a mutating self parameter". If the context is read-only, the value of self could be copied or shared and in either case there wouldn't be a reference cycle.




回答4:


You can solve the problem by creating a weak reference to the object which is captured by the closure.

Here is your example without memory leak:

import Foundation
import RxSwift

struct WithoutLeak {

    var someState: String = "initial value"
    var someVariable: Variable<String> = Variable("some stuff")

    let bag = DisposeBag()

    mutating func someFoo() {

        weak let weakSomeState = someState // <-- create a weak reference outside the closure

        someVariable.subscribeNext { person in

            weakSomeState = "something" // <-- use it in the closure
        }
        .addDisposableTo(bag)
    }
}


来源:https://stackoverflow.com/questions/34406196/swift-struct-memory-leak

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!