这个问题已经开放了很长时间,我现在有足够的信心回答它。
MM的不同等级:
硬件内存
在带有 ARC 的 Swift 中,我们无法清理实际的硬件内存。我们只能让操作系统为我们做到这一点。一方面是使用正确的代码(optionals 和weak),另一方面是为操作系统创造时间来完成它的工作。
想象一下,我们有一个无限期地在所有线程上运行的函数。它只做一件事,加载图像,转换为黑白并保存。
所有图像的最大值为几 mb,并且该功能不会产生软件内存泄漏。
因为图像没有固定的大小并且可能有不同的压缩,所以它们没有相同的占用空间。
此功能将始终使您的应用程序崩溃。
这种“硬件”内存泄漏是由函数总是占用下一个可用的内存插槽引起的。
操作系统不会介入“实际清理内存”,因为没有空闲时间。在每次通过之间设置延迟完全解决了这个问题。
语言特定的MM
选角
有些操作对内存没有影响,有些则有:
let myInt : Int = 1
Float(myInt) // this creates a new instance
尝试转换:
(myInt as Float) // this will not create a new instance.
引用类型与值类型 |类与结构
两者各有优劣。
结构是内存密集型的,因为它们是值类型。
这意味着它们在分配给另一个实例时复制它们的值,从而有效地将内存使用量加倍。
对此没有修复/解决方法。这就是 Structs 结构的原因。
类没有这种行为,因为它们是引用类型。他们在分配时不会复制。
相反,他们为同一对象创建另一个引用。 ARC 或 Automatic Reference Counting 用于跟踪这些引用。
每个对象都有一个引用计数器。每次分配它,它都会增加一个。每次你设置一个对 nil 的引用时,封闭的函数结束,或者封闭的 Object 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。这允许您更改传递给函数的结构,而无需复制结构。这可能会节省内存,具体取决于您传递的内容以及您在函数中执行的操作。
这也是一种无需使用元组即可获得多个返回值的好方法。
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
面向对象编程使用具有变化/变异状态的对象。不是创建新值,而是更新旧值。
函数式编程可以使用更多内存。
example on FP
选项
选项允许您将事物设置为零。这将降低类的引用计数或取消初始化结构。将事物设置为 nil 是清理内存的最简单方法。这与 ARC 齐头并进。一旦你将一个类的所有引用都设置为 nil,它将 deinit 并释放内存。
如果您不将实例创建为可选项,则数据将保留在内存中,直到封闭函数结束或封闭类取消初始化。你可能不知道这什么时候会发生。选项让您可以控制哪些内容可以存活多久。
API MM
许多“内存泄漏”是由具有您可能没有调用的“清理”功能的框架引起的。
一个很好的例子是UIGraphicsEndImageContext() 上下文将保留在内存中,直到调用此函数。当创建上下文的函数结束时,或者所涉及的图像设置为 nil 时,它不会清理。
另一个很好的例子是关闭 ViewControllers。转入一个 VC 然后转回可能是有意义的,但转义实际上创建了一个 VC。 segue back 不会破坏 VC。调用dismissViewControllerAnimated() 将其从内存中删除。
阅读类引用并仔细检查没有“清理”功能。
如果您确实需要仪器来查找泄漏,请查看此问题的其他答案。