Swift的ARC和内存泄漏

栏目: Swift · 发布时间: 6年前

内容简介:Swift引入了一项称为自动引用计数(ARC)的强大功能,可以处理应用程序的大部分内存管理。然而,初学者程序员通常不知道它是如何运作的。ARC负责分配和释放对象使用的内存。为了使其自动化,ARC计算对您创建的变量的引用。删除对象的引用后,ARC将自动为您释放对象。

Swift引入了一项称为自动引用计数(ARC)的强大功能,可以处理应用程序的大部分内存管理。然而,初学者 程序员 通常不知道它是如何运作的。

ARC负责分配和释放对象使用的内存。为了使其自动化,ARC计算对您创建的变量的引用。

class Object {
    var name: String
}

let object = Object()       //ARC object reference count: 1
var reference2 = object     //ARC object reference count: 2
var reference3 = reference1 //ARC object reference count: 3
var reference4 = object     //ARC object reference count: 4
复制代码

删除对象的引用后,ARC将自动为您释放对象。

let object = null        //ARC object reference count: 3
var reference2 = null    //ARC object reference count: 2
var reference3 = null    //ARC object reference count: 1
var reference4 = null    //ARC object reference count: 0
复制代码

当ARC释放内存时,将调用对象的deinit() 方法。

内存泄漏

在Swift中,内存泄漏存在与循环引用中。当两个对象彼此保持强引用时,会发生循环引用。

这通常发生在逃逸闭包中。

在使用逃逸闭包写回调时,任何使用任何当前类的方法或者self都将创建对该实例的强引用,从而造成循环引用,因为闭包将保持对类的强引用,并且类将保留对闭包所在类的强引用。

下面的示例显示了使用逃逸闭包时的常见的循环引用。

class NetworkHelper {
    func getFeed(completion: @escaping ([FeedItem]) -> Void) {
        Alamofire.request(…).responseJSON { (response) in
            if let value = response.result.value {
                if let json = JSON(value)[Constants.items].array {
                    completion(json.flatMap(FeedItem.init))
                }
            }
        }
    }
}
class FeedViewController {
    var tableView: UITableViewController
    var feedItems: [FeedItem]
    var networkHelper: NetworkHelper
    override func viewDidLoad() {
        ...
        networkHelper.getFeed() { items in
            self.feedItems = items
            self.tableView.reloadData()
        }
    }
}
复制代码

在上面的示例中,FeedViewController通过变量networkHelper保存对NetworkHelper的强引用。然后,对FeedViewController的引用作为闭包传递给networkHelper,从而创建从networkHelper到FeedViewController的强引用。

Swift的ARC和内存泄漏

即使没有其他对这些对象的引用,ARC也永远无法清理FeedViewController或NetworkHelper。这将导致两个对象存在,直到用户关闭应用程序。如果重新创建FeedViewController,则可能再次发生循环引用,从而使问题更加严重。

弱引用和无主引用

要中断循环引用,必须从传递给networkHelper的闭包中删除对FeedViewController的强引用。这是通过使用弱或无主的自我来完成的。

weak关键字通过不递增ARC的引用计数来创建对变量的弱引用。但是,由于它不是强引用,因此无法保证在执行闭包时对象将存在。因此,只要您使用 weak 关键字,该变量就是可选的。

unowned关键字也不会增加ARC的引用计数,但它也不是可选的。但是由于它没有对变量的强引用,它可能不存在并且可能完全指向其他东西。除非两个对象始终像计算机及其处理器一样存在,否则不应使用 unowned

要修复上面的示例,您只需指定self是弱引用,保留周期将被破坏。

class FeedViewController {
var tableView: UITableViewController
    var feedItems: [FeedItem]
    var networkHelper: NetworkHelper
override func viewDidLoad() {
        ...
        networkHelper.getFeed() { [weak self] items in
            self?.feedItems = items
            self?.tableView.reloadData()
        }
    }
}
复制代码

调试内存管理

XCode具有很好的功能,可以检查应用程序运行时存在的内存使用情况,引用和对象实例。

Swift的ARC和内存泄漏

Xcode Memory Graph暂停您的应用程序执行并显示当前存在的所有对象。您还可以选择对象的实例,并查看哪些对象包含对它的引用。

当内存泄漏确实发生时,Xcode甚至经常用紫色的解释点突出显示有问题的类。添加一个简单的保留周期并多次执行该操作会产生如下所示的内存图。

Swift的ARC和内存泄漏

单击其中一个实例进一步显示闭包持有对DetailsViewController的引用,我在其中创建了以下循环引用。

let retainCycle = RetainCycle()
override func viewDidLoad() {
    retainCycle.keepMe { 
        self.view.backgroundColor = .white 
    }
}
class RetainCycle {
    func keepMe(closure: @escaping () -> Void) {
        URLSession.shared.dataTask(...) { (data, _, _) in 
            closure()
        }
    }
}
复制代码

因此,下次创建闭包时,无论是NotificationCenter上的观察者,网络调用还是其他异步任务,都要记得在内存泄漏之前之前检查一下。


以上所述就是小编给大家介绍的《Swift的ARC和内存泄漏》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

编程珠玑

编程珠玑

Jon Bentley / 人民邮电出版社 / 2006-11 / 28.0

《编程珠玑》第一版是我早期职业生涯中阅读过的对我影响较大的书籍之一,在书中首次接触到的很多观点都让我长期受益。作者在这一版本中做了重要更新,新增加的很多例子让我耳目一新。——Steve McConnell,《代码大全》作者  如果让程序员列举出他们喜欢的书籍,Jon Bentley的《编程珠玑》一定可以归于经典之列。如同精美的珍珠出自饱受沙砾折磨的牡蛎,程序员们的精彩设计也来源泉于曾经折磨他们的实......一起来看看 《编程珠玑》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

Base64 编码/解码
Base64 编码/解码

Base64 编码/解码

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器