Swift Struct内存泄漏

fin*_*elp 29 memory-leaks swift rx-swift

我们试图尽可能地使用Swift结构.我们也在使用RxSwift,它具有关闭的方法.当我们有一个结构创建一个引用self的闭包时,它会创建一个强大的引用循环.

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)
    }
}
Run Code Online (Sandbox Code Playgroud)

我怎么知道这个?如果我创建100,000个DoesItLeak对象并在每个对象上调用someFoo(),我相信我有100,000个具有强引用周期的对象.换句话说,当我摆脱包含这些对象的DoesItLeak数组时,对象会留在内存中.如果我不调用someFoo(),则没有问题.

变量是一个类.所以,我可以通过使用xcode的Instruments的Allocations和Variable <String>中的过滤来看到这个内存问题

按变量过滤

在此输入图像描述

如果我尝试使用[弱自我],如下所示,我得到一个编译器错误:

someVariable.subscribeNext { [weak self] person in
Run Code Online (Sandbox Code Playgroud)

编译错误是"弱不能应用于非类型"

在实际/非示例代码中,我们通过self访问方法和变量,这是一个内存问题.

如何在保持DoesItLeak结构的同时解决此内存问题?

谢谢你的帮助.

fin*_*elp 12

正如Darren在评论中所说的那样:" IsItLeak不能成为一个结构 "我们不能DoesItLeak成为一个结构并安全地解决强引用循环问题.

堆栈框架上存在类似结构的值类型.闭包和类是引用类型.

正如" 闭包强引用循环"部分所示:

这种强引用循环的发生是因为闭包(如类)是引用类型.

由于struct具有Variable 并且引用的闭包self被存储到Variable类中subscribeNext,因此它创建了强引用循环.请参阅自动引用计数 Apple文档中的"为闭包解决强引用周期" .

  • “在堆栈框架上存在诸如结构之类的值类型。”通常情况并非如此。通过转义闭包引用的值类型将必须移到堆中。对于敏捷的程序员,堆和堆栈都应该完全抽象。 (2认同)
  • 所以这个答案基本上是说:“你必须使用类来消除内存泄漏”? (2认同)

小智 5

对于仍然面临此问题的任何人。

1)[weak self]不可能,因为Struct是,value type而不是a Reference type,所以没有这样的指针。

2)这里泄漏的主要问题是您试图访问self.someState = something完成块内的Struct属性,这将基本上在分配时创建结构的新副本。

您不应在完成块内访问Struct属性。


Nik*_*ner 5

您可以通过创建对闭包捕获的对象的弱引用来解决该问题。

这是没有内存泄漏的示例:

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)
    }
}
Run Code Online (Sandbox Code Playgroud)