Swift 管理内存

Swift Managing Memory

此问题已清理,重要信息已移至下面的答案。


我有一些关于内存管理的问题。

我正在构建一个照片编辑应用程序。因此保持低内存使用率很重要。 另外,我不会 post 编写代码,因为在做一件特定的事情时我没有大的内存泄漏。我只是失去了几个 KB's/MB's 与发生的一切。翻过数万行代码来查找千字节可不是一件有趣的事 ;)

我的应用程序使用核心数据、大量 cifilter 内容、位置和基础知识。

我的第一个视图只是一个表格视图,它占用了我大约 5mb 的内存。 然后你拍一些照片,应用一些过滤器,这被保存到核心数据,然后你回到第一个视图。

是否有可能真正摆脱内存中除了驱动第一个视图所需的数据之外的所有内容。 (非常节省和很棒的 5mb)

或者即使您将所有内容都设置为零,是否总会留下一些东西?


奖金问题: UIImageJPEGRepresentationUIImagePNGRepresentation 之间的文件大小/cpu 负载是否存在差异? 我知道您可以使用 JPEG 方法设置压缩质量(cpu/gpu 更难?)。

尽一切可能减少内存压力。


更新:

有人向我指出问题可能过于模糊。

我在某些时候遇到的问题如下:


P.s 这都是在 iPhone 4s 上测试的,不是模拟器。

这里有一个表情包可以稍微缓解一下这个网站的气氛。

在 Xcode 的 top-right 角单击您的应用程序名称。

在弹出的菜单中点击 'edit scheme'。

确保在左侧选择了 'RUN',然后单击 window 顶部附近的诊断选项卡。

下'memory management'header勾选'enable Guard Malloc'

您可能还想尝试检查 'logging' header

下的 'distributed objects' 和 'malloc stack'

可以找到有关 guard malloc、guard edges 和 scribble 的更多信息 here



希望这有帮助!

这个问题已经开放很久了,我现在有足够的信心来回答它了。


不同级别的MM:

硬件内存

在 Swift 和 ARC 中,我们无法清理实际的硬件 ram。我们只能让 OS 为我们做到这一点。一部分是使用正确的代码(optionalsweak),另一部分是为 OS 创造时间来完成它的工作。

假设我们有一个无限期地在所有线程上运行的函数。它做一件事,加载图像,转换为 black/white 并保存。 所有图像最大为几 mb,并且该功能不会造成软件内存泄漏。 因为图像没有固定大小并且可能有不同的压缩,所以它们不具有相同的占用空间。 此功能将始终使您的应用程序崩溃。

此“硬件”内存泄漏是由于函数总是占用下一个可用内存插槽引起的。

OS不会介入“实际清理内存”,因为没有空闲时间。在每次通过之间放置一个延迟可以完全解决这个问题。


语言特定 MM

铸造

有些操作不会影响内存,有些会:

let myInt : Int = 1
Float(myInt) // this creates a new instance

尝试投射:

(myInt as Float) // this will not create a new instance.

引用类型与值类型 | Classes 与结构

两者各有优缺点。

结构 是内存密集型的,因为它们是值类型。 这意味着它们在分配给另一个实例时复制它们的值,有效地双倍内存使用。 对此没有修复/解决方法。这就是使 Structs 成为 Structs 的原因。

Classes 没有这种行为,因为它们是 引用类型 。他们在分配时不会复制。 相反,他们创建了 另一个对 相同对象 的引用 ARCAutomatic Reference Counting 是用来跟踪这些引用的。 每个对象都有一个引用计数器。每次分配它时,它都会增加一个。每次设置对 nil 的引用、封闭函数结束或封闭对象 deinits 时,计数器都会下降。

当计数器达到 0 时,对象被取消初始化。

有一种方法可以防止实例取消初始化,从而造成泄漏。这称为 强引用循环

Good explanation of Weak

class MyClass {

    var otherClass : MyOtherClass?

    deinit {
        print("deinit") // never gets called
    }
}

class MyOtherClass {

    var myclass : MyClass?

    deinit {
        print("deinit") // never gets called
    }
}

var classA : MyClass? = MyClass()

// sorry about the force unwrapping, don't do it like this
classA!.otherClass = MyOtherClass()
classA!.otherClass!.myclass = classA // this looks silly but in some form this happens a lot

classA = nil
// neither the MyClass nor the MyOtherClass deinitialised and we no longer have a reference we can acces. Immortalitiy reached they have.

设置一个参考 weak

class MyOtherClass {

    weak var myclass : MyClass?

    deinit {
        print("deinit") // gets called
    }
}

输入输出

函数捕获传递给它们的值。但也可以将这些值标记为 inout。这允许您更改传递给函数的 Struct 而无需复制 Struct。这可能会节省内存,具体取决于您传递的内容和您在函数中执行的操作。

这也是在不使用元组的情况下拥有多个 return 值的好方法。

var myInt : Int = 0

// return with inout
func inoutTest(inout number: Int) {

    number += 5

}

inoutTest(&myInt)
print(myInt) // prints 5

// basic function with return creates a new instance which takes up it's own memory space
func addTest(number:Int) -> Int {

    return number + 5

}

函数式编程

状态是随时间变化的价值

函数式编程是面向对象编程的对应部分。函数式编程使用不可变状态。

关于此的更多信息here

面向对象编程使用具有 changing/mutating 状态的对象。不是创建新值,而是更新旧值。

函数式编程可以使用更多的内存。

example on FP


可选

Optionals 允许您将 thing 设置为 nil。这将降低 Classes 的引用计数或取消初始化结构。设置为 nil 是清理内存最简单的方法。这与 ARC 密切相关。一旦将 Class 的所有引用都设置为 nil ,它将取消初始化并释放内存。

如果您不创建实例作为可选,数据将保留在内存中,直到封闭函数结束或封闭的 class deinits。你可能不知道这会在什么时候发生。 Optionals 可以让你控制什么东西能活多久。


API MM

许多 "memory leaks" 是由 Frameworks 引起的,它们具有您可能没有调用的“清理”功能。 一个很好的例子是 UIGraphicsEndImageContext() 上下文将保留在内存中,直到调用此函数。当创建上下文的函数结束时,或者当涉及的图像设置为 nil 时,它不会清理。

另一个很好的例子是关闭 ViewController。转至一个 VC 然后转回去可能是有意义的,但转场实际上创建了一个 VC。后退不会破坏 VC。调用 dismissViewControllerAnimated() 将其从内存中删除。

阅读 Class 参考资料并仔细检查没有“清理”功能。


如果您确实需要 Instruments 来查找泄漏,请查看关于此问题的其他答案。