快速管理内存 [英] Swift Managing Memory

查看:73
本文介绍了快速管理内存的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

This question was cleaned up and the important info moved to the answer below.

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

I have some questions about memory management.

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

I am building a photo editing app. So keeping memory usage low is important. Also I am not going to post code because I do not have a big memory leak when doing one specific thing. I just lose a couple of KB's/MB's with everything that happens. And going over tens of thousands of lines of code to find kilobytes is no fun ;)

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

my app uses core data, lots of cifilter stuff, location, and the basics.

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

My first view is just a tableview which costs me about 5mb of memory. Then you take some photos, apply some filters, this gets saved to core data and then you go back to that first view.

是否有可能真正消除内存中的所有内容,除了驱动该第一个视图所需的数据外. (非常节省且很棒的5mb)

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

Or will there always be something left behind, even if you set everything to nil?

奖金问题: UIImageJPEGRepresentationUIImagePNGRepresentation之间的文件大小/cpu负载有区别吗? 我知道您可以使用JPEG方法设置压缩质量(在cpu/gpu上更难使用?).

Bonus Question: is there a difference in file size / cpu load between UIImageJPEGRepresentation and UIImagePNGRepresentation? I know you can set a compression quality with the JPEG method (harder on the cpu/gpu?).

只是尽一切可能减少内存压力.

Just trying to reduce memory pressure by all means possible.

更新:

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

It was pointed out to me that the question might be too vague.

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

The problems I was having at some point or another were the following:

  • 某些时候峰值内存使用率过高
  • 导航到第二个ViewController并返回会导致泄漏
  • 编辑图像会导致内存泄漏.
  • 将过滤器应用于4-5个以上的图像会导致崩溃,原因是内存不足,此时不再有内存泄漏. (已在工具中验证)

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

P.s this was all tested on an iPhone 4s , not the simulator.

这里有一个模因减轻了网站的情绪.

推荐答案

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

This question has been open long enough and I now feel confident enough to answer it.

硬件内存

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

In Swift with ARC we have no way to clean up the actual hardware ram. We can only make it possible for the OS to do that for us. One part is using the right code (optionals and weak) the other part is creating time for the OS to do it's job.

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

Imagine we have a function that runs on all threads indefinitely. It does one thing, load an image, convert to black/white and save. All images max at a couple of mb’s and the function creates no Software Memory Leaks. Because images don’t have a set size and might have different compression they don’t have the same footprint. This function will always crash your app.

此硬件"内存泄漏是由于该功能始终占用下一个可用的内存插槽引起的.

This "Hardware" Memory Leak is caused by the function always taking the next available slot of memory.

由于没有空闲时间,因此操作系统不会介入以实际清理内存".在每遍之间放置延迟可以完全解决此问题.

The OS does not step in to "actually clean the memory" because there is no idle time. Putting a delay between each pass completely fixes this.

铸造

某些操作对内存没有影响,而其他操作却有影响:

Some operations don’t have an impact on memory, others do:

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

改为尝试投射:

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


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

两者都有优点和危险.

结构占用大量内存,因为它们是值类型. 这意味着当它们分配给另一个实例时,它们会复制其值,从而有效地将内存使用量增加一倍. 没有针对此的修复/解决方法.这就是构造Structs的原因.

Structs are memory intensive because they are Value Types. This means they copy their values when assigned to another instance, effectively doubling memory usage. There is no fix / work around for this. It is what makes Structs Structs.

没有这种行为,因为它们是引用类型.他们在分配后不会复制. 相反,他们为相同对象创建了另一个引用. ARC 自动引用计数是跟踪这些引用的方法. 每个对象都有一个参考计数器.每次分配它时,它都会增加一.每次将引用设置为nil时,封闭函数都会终止,或者封闭对象会取消初始化,计数器会下降.

Classes don’t have this behaviour because they are Reference Types. They don’t copy when assigned. Instead they create another reference to the same object. ARC or Automatic Reference Counting is what keeps track of these references. Every Object has a reference counter. Each time you assign it, it goes up by one. Each time you set a reference to nil, the enclosing function ends, or the enclosing Object deinits, the counter goes down.

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

When the counter hits 0 the object is deinitialised.

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

There is a way to prevent an instance from deinitialising, and thus creating a leak. This is called a Strong Reference Cycle.

对弱点的很好解释

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

Functions capture the values passed to them. But it is also possible to mark those values as inout. This allows you to change a Struct passed to a function without copying the Struct. This might save memory, depending on what you pass and what you do in the function.

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

It is also a nice way of having multiple return values without using tuples.

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

}


功能编程

状态就是时间的价值

功能编程是面向对象编程的反义部分.函数式编程使用不可变状态.

Functional programming is the counter part of Object Oriented programming. Functional programming uses Immutable state.

有关此面向对象的编程使用具有变化/变异状态的对象.而不是创建新值,而是更新旧值.

Object Oriented programming uses Objects that have changing/mutating states. Instead of creating a new value, the old values are updated.

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

Functional Programming can use more memory.

FP上的示例

可选

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

Optionals allow you to set thing to nil. This will lower the reference count of Classes or deinitialise Structs. Setting things to nil is the easiest way to clean up memory. This goes hand in hand with ARC. Once you have set all references of a Class to nil it will deinit and free up memory.

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

If you don’t create an instance as an optional, the data will remain in memory until the enclosing function ends or the enclosing class deinits. You might not know when this will happen. Optionals give you control over what stays alive for how long.

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

Many "memory leaks" are cause by Frameworks that have a "clean up" function that you might not have called. A good example is UIGraphicsEndImageContext() The Context will stay in memory until this function is called. It does not clean up when the function that created the context ends, or when the image involved is set to nil.

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

Another good example is dismissing ViewControllers. It might make sense to segue to one VC and then segue back, but the segue actually creates a VC. A segue back does not destroy a VC. Call dismissViewControllerAnimated() to remove it from memory.

阅读类参考,并仔细检查是否没有清理"功能.

Read the Class References and double check there are no "clean up" functions.

如果您确实需要工具来查找泄漏,请查看有关此问题的其他答案.

If you do need Instruments to find a leak, check out the other answer on this question.

这篇关于快速管理内存的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆