Swift的ARC和内存泄漏

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

内容简介: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和内存泄漏》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

R数据科学

R数据科学

[新西兰] 哈德利 • 威克姆、[美] 加勒特 • 格罗勒芒德 / 陈光欣 / 人民邮电出版社 / 2018-7 / 139.00元

本书的目标是教会读者使用最重要的数据科学工具,从而为实施数据科学奠定坚实的基础。读完本书后,你将掌握R语言的精华,并能够熟练使用多种工具来解决各种数据科学难题。每一章都按照这样的顺序组织内容:先给出一些引人入胜的示例,以便你可以整体了解这一章的内容,然后再深入细节。本书的每一节都配有习题,以帮助你实践所学到的知识。一起来看看 《R数据科学》 这本书的介绍吧!

JS 压缩/解压工具
JS 压缩/解压工具

在线压缩/解压 JS 代码

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

SHA 加密
SHA 加密

SHA 加密工具