/ iOS

iOS App 的内存释放问题

循环引用

这个是非常常见的一个问题,你可以从这里获取我们的演示 Demo

例如我们创建了一个 BadGuy() 功能只有一个,可以存一个闭包。

class BadGuy: NSObject {
    
    var holdAction: (() -> Void)?
}

然后我们从外层 ViewController 通过 Push 的方法进入 DetailViewController,DetailViewController 里创建了 10000 个 UIView 来占用些内存,badGuy 通过闭包修改了下 DetailViewController 的 hello 的属性。

import UIKit

class DetailViewController: UIViewController {
    
    var badGuy = BadGuy()

    var hello: String?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        badGuy.holdAction = {
            self.hello = "Hello"
        }
        
        var a = 10000
        
        while a > 0 {
            a -= 1
            
            view.addSubview(UIView())
        }
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    deinit {
        print("Deinit")
    }

}

在 push 前内存占用是 20M, push 后内存占用为 32.3M 反复进行几次后,内存占用会不停往上飙升,deinit 方法的打印也不会执行。

原因

holdAction 这个闭包引用了 DetailViewController 的 hello,而这个 self.hello 又因为强引用 DetailViewController 的原因,导致当你返回的时候,DetailViewController 并不会被释放。

强引用

只要有任何对象强引用对象 A,ARC 就不会摧毁 A。

弱引用

弱引用对象 B 的情况下,若没有其他物体强引用对象 B,ARC 会摧毁 B。

解药

在 GoodViewController 里我们可以通过更改强引用为弱引用的方法,来避免这种情况。

import UIKit

class GoodViewController: UIViewController {

    var badGuy = BadGuy()

    var hello: String?
    
    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
        
        badGuy.holdAction = { [weak self] in
            self?.hello = "Hello"
        }
        
        var a = 10000
        
        while a > 0 {
            a -= 1
            
            view.addSubview(UIView())
        }
    }
    
    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    
    deinit {
        print("Deinit")
    }
}

做了这样的修改后,反复进行返回,进入的测试,也不会导致内存暴增了,deinit 也会在你返回的时候成功打印。

你的 App 是否有这个问题呢?

iOS App 的内存释放问题
Share this