迭代错误数组时发生Swift内存泄漏

我对Swift比较陌生,所以我希望我不要问一个愚蠢的问题。

我有一些代码实例化一个Errortypes的数组,这些代码稍后将被迭代并打印到控制台。 当使用“泄漏”工具通过仪器运行此代码时,它显示_SwiftNativeNSError泄漏。 如果将数组types从[Error]更改为[Any] ,则泄漏消失,即使它实际上仍保持符合Error的对象。 泄漏不能用我试过的任何其他数据types或协议重现。

以下是一些示例代码:

 class myLeak { lazy var errors = [Error]() enum err: Error { case myFirstError } func doSomething() { errors.append(err.myFirstError) for error in errors { print(String(describing: error)) } } } // call with let myleak = myLeak(); myleak.doSomething() 

调用doSomething()函数立即创build泄漏。 将[Error]()切换到[Any]()可以解决泄漏问题,但是我并不满意这个解决scheme而不理解底层问题。 这个问题也可以通过改变[Error]()来实现Error协议: [err]() 。 我也尝试创build自己的自定义协议,只是为了certificate这是由Error引起的,我只能在使用Error时重现问题; 我自己的自定义协议没有performance出这种行为。

最初,我的代码使用forEach循环迭代数组,但是我然后尝试重写它以使用forEach中的闭包导致问题的情况下的标准for循环,但是这不起作用。

我怀疑这可能是一个Swift错误(在这种情况下,我会为它打开一个问题),但也有一个机会,我错过了一个关键的理解。 如果我正在做的是不好的做法,我想了解为什么。

更新

在与苹果工程师Joe Groff交谈之后,这是您可能遇到的错误: https : //bugs.swift.org/browse/SR-6536

原始答复

我已经玩了一下你的代码,我认为这个问题是由于Errortypes。 实际上,如果您使用ErrorMyError作为数组的types,则可以使用Josh的代码来查找不同的行为。

我猜这个问题出现了,因为deinit调用不会被转发到CustomObject因为Error只是一个协议,并不知道底层的类。 而MyError是。 我们可以等待其他人澄清这一行为。

为了简单起见,我在这里使用一个游乐场。 看到我甚至没有试图打印错误值。

 import UIKit class ViewController: UIViewController { var errors: [Error] = [] // change to MyError to see it working enum MyError: Error { case test (CustomObject) } class CustomObject { deinit { print("deiniting") } } override func viewDidLoad() { super.viewDidLoad() let testerror = MyError.test(CustomObject()) errors.append(testerror) errors.removeAll() } } do { let viewController = ViewController() // just for test purposes ;) viewController.viewDidLoad() } 

我testing了你的代码,它看起来像String(describing)语句导致string保留错误,这只是奇怪的。 下面是我可以告诉的:我创build了一个关联的对象,当它被去初始化时打印出来。

 import UIKit class ViewController: UIViewController { var errors = [Error]() override func viewDidLoad() { super.viewDidLoad() class CustomObject { deinit { print("deiniting") } } enum MyError: Error { case test (CustomObject) } let testerror = MyError.test(CustomObject()) errors.append(testerror) for error in errors { //print(String(describing: error)) } errors.removeAll() } } 

当打印不运行时,确信只要从数组中删除错误,关联的对象就会被初始化,输出是:

 deiniting 

取消打印,输出成为:

 test(CustomObject #1 in stack.ViewController.viewDidLoad() -> ()) 

起初我以为这是打印这是问题,但如果我重构:

 errors.forEach{print($0)} 

我得到的输出:

 test(CustomObject #1 in stack.ViewController.viewDidLoad() -> ()) deiniting 

但是,如果我改变它:

 errors.map {String(describing:$0)}.forEach{print($0)} 

然后deinit不再被称为:

 test(CustomObject #1 in stack.ViewController.viewDidLoad() -> ()) 

怪事。 也许是雷达 ?