当前位置: 首页 > 面试题库 >

快速管理内存

穆锋
2023-03-14
问题内容

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

我对内存管理有一些疑问。

我正在构建照片编辑应用程序。因此,保持较低的内存使用量很重要。另外,我不打算发布代码,因为在做一件特定的事情时,我不会发生大的内存泄漏。我将所有发生的一切都丢失了几KB
/ MB。遍历数万行代码以查找千字节并不有趣;)

我的应用使用了核心数据,许多cifilter内容,位置和基础知识。

我的第一个视图只是一个表视图,它占用了我约5mb的内存。然后,您拍摄一些照片,应用一些滤镜,然后将其保存到核心数据中,然后返回该第一个视图。

是否有可能真正摆脱内存中的所有内容,但驱动该第一个视图所需的数据除外。 (非常节省和令人敬畏的5mb)

还是即使您将所有内容都设置为零,也总会遗留下一些东西吗?

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

尽一切可能尝试降低内存压力。

更新:

有人向我指出,这个问题可能太含糊了。

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

  • 在某些时候,峰值内存使用率太高
  • 导航到第二个ViewController并返回会导致泄漏
  • 编辑图像会导致内存泄漏。
  • 由于内存不足,对超过4-5个图像应用滤镜会导致崩溃,此时不再有内存泄漏。(已在乐器中验证)

附言:所有这些都在iPhone 4s而非模拟器上进行了测试。

这里有一个模因来减轻这个网站上的心情。


问题答案:

这个问题已经开放了很长时间,我现在有足够的信心回答这个问题。

不同级别的MM:

硬体记忆体

在带有 ARC的
Swift中,我们无法清除实际的硬件内存。我们只能让OS为我们做到这一点。一部分是使用正确的代码(optionalsweak),另一部分是为操作系统创造时间来完成其工作。

假设我们有一个函数可以无限期地在所有线程上运行。它做一件事,加载图像,转换为黑白并保存。所有映像的最大大小只有几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
自动参考计数
是跟踪这些参考的方法。每个对象都有一个参考计数器。每次分配它时,它都会增加一。每次您将引用设置为nil时,封闭函数都会终止,或者封闭对象会取消初始化,计数器会下降。

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

有一种方法可以防止实例初始化,从而防止泄漏。这称为 强参考周期

弱的很好的解释

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。这可能会节省内存,具体取决于您通过的内容和在函数中所做的事情。

这也是不使用元组而具有多个返回值的一种好方法。

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

}

功能编程

状态就是时间的价值

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

在这里更多

面向对象的编程使用具有变化/变异状态的对象。代替创建新值,而是更新旧值。

功能编程可以使用更多的内存。

FP上的示例

选装件

可选选项使您可以将事物设置为零。这将减少类的引用计数或取消初始化结构。将内容设置为nil是清理内存的最简单方法。这与ARC紧密相关。将类的所有引用设置为nil后,它将取消初始化并释放内存。

如果不创建实例作为可选实例,则数据将保留在内存中,直到封闭函数结束或封闭类取消初始化为止。您可能不知道什么时候会发生。可选项使您可以控制存活的时间。

API MM

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

另一个很好的例子是关闭ViewController。先选择一个VC,然后再选择回来,这很有意义,但实际上是创建了一个VC。退回不会破坏VC。调用dismissViewControllerAnimated()以将其从内存中删除。

阅读类参考,并仔细检查是否没有“清理”功能。

如果您确实需要仪器来发现泄漏,请查看此问题的其他答案。



 类似资料:
  • 我知道这样一个事实:如果将< code>closure赋给一个类的属性,并且在闭包内部使用了该类的实例属性,那么它可以创建< code>retain cycles。但是 1)闭包没有分配给类属性,而是作为参数传递给单例的类方法,这是怎么回事? 2) 在这种情况下如何管理内存? 在我的控制器(UIViewController)的方法中,我有类似的东西:

  • 对于一个基于图论的框架来说,节点和边是最小的部件。实际应用中,这些部件构成了各种有向图。比如一个有环图,它的数据流动就是一个环形,部件之间的持有关系如果不能很好的处理,那么可能就会存在内存问题。EasyReact 的内存管理逻辑非常简单,也非常精巧。可以让框架使用者无需关注太多的细节即可轻松的使用,而不必担心本框架涉及的内存方面的问题。 中间节点 节点包含了 fork、map、filter、ski

  • 在计算系统中,通常存储空间可以分为两种:内部存储空间和外部存储空间。内部存储空间通常访问速度比较快,能够按照变量地址随机地访问,也就是我们通常所说的 RAM(随机存储器),可以把它理解为电脑的内存;而外部存储空间内所保存的内容相对来说比较固定,即使掉电后数据也不会丢失,这就是通常所讲的 ROM(只读存储器),可以把它理解为电脑的硬盘。 计算机系统中,变量、中间数据一般存放在 RAM 中,只有在实际

  • 内存生命周期 垃圾回收 垃圾回收在计算机科学中是一种自动的内存管理机制。当一个计算机上的动态内存不再需要时,就应该予以释放以让出内存,这种内存资源管理称为垃圾回收。垃圾回收器可以让程序员减轻许多负担,也减少程序员犯错的机会。 特征 垃圾回收基于两个原理: 考虑某个对象在未来的程序运行中将不会被访问; 向这些对象要求归还内存。 然而,最主要的也是最艰难的部分就是找到「所分配的内存确实已经不再需要了」

  • 主要内容:一、redis的内存管理,二、源码分析,三、总结一、redis的内存管理 一般来说,稍微有点规模的软件,都会自己搞一块内存管理,原因很简单,统一管理内存,适应自己的场景。其实按大牛们的话,这未必是最优选择,实在是小看了写库的那群大牛们。不过说归说,人家写也不会给你报备,想写自然就写了。Redis就听从了大牛的看法,使用了底层更好的内存分配库,根据情况使用tmalloc,jemalloc 以及glibc中的 malloc(pmalloc)。 一般

  • 本章描述 Linux 内核中的内存管理。在本章中你会看到一系列描述 Linux 内核内存管理框架的不同部分的帖子。 内存块 - 描述早期的 memblock 分配器。 固定映射地址和 ioremap - 描述固定映射的地址和早期的 ioremap 。 kmemcheck - 第三部分描述 kmemcheck 工具。