迭代错误数组时发生Swift内存泄漏
我对Swift比较陌生,所以我希望我不要问一个愚蠢的问题。
我有一些代码实例化一个Error
types的数组,这些代码稍后将被迭代并打印到控制台。 当使用“泄漏”工具通过仪器运行此代码时,它显示_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
原始答复
我已经玩了一下你的代码,我认为这个问题是由于Error
types。 实际上,如果您使用Error
或MyError
作为数组的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() -> ())
怪事。 也许是雷达 ?