迭代错误数组

时间:2017-12-20 11:00:20

标签: ios arrays swift memory-leaks

我对Swift来说比较新,所以我希望我不会问一个愚蠢的问题。

我有一些代码实例化了Error类型的数组,稍后会迭代并打印到控制台。使用"泄漏"通过仪器运行此代码时仪器,它显示_SwiftNativeNSError的泄漏。如果我将数组类型从[Error]更改为[Any],则泄漏消失,即使它实际上仍然保持符合Error的对象。使用我尝试过的任何其他数据类型或协议都无法重现泄漏。

以下是一些示例代码:

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()函数会立即产生泄漏。将[Error]()切换为[Any]()可以解决泄漏问题,但我对此并不满意,因为它不是解决潜在问题的解决方案。通过将[Error]()更改为实现Error协议的枚举[err](),也可以解决此问题。我还尝试创建自己的自定义协议,以证明这是由Error专门引起的,而我只能在使用Error时重现问题。我自己的自定义协议没有表现出这种行为。

最初,我的代码使用forEach循环来迭代数组,但我尝试重写它以使用标准for循环,以防forEach中的闭包导致问题,但这没有用。

我怀疑这可能是一个Swift错误(在这种情况下,我会为它开一个问题),但是我也错过了一个关键部分理解。如果我所做的是不好的做法,我想了解原因。

3 个答案:

答案 0 :(得分:1)

我测试了你的代码,看起来String(describing)语句导致字符串保留错误,这很奇怪。以下是我的判断:我创建了一个关联对象,该对象在被取消初始化时打印出来。

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() -> ())

怪诞。也许提交radar

答案 1 :(得分:1)

<强>更新

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

原始答案

我已经使用了您的代码,我认为问题是由于Error类型造成的。 实际上,如果您使用ErrorMyError作为数组类型,则可以使用Josh的代码来查找不同的行为。

我想问题出现了,因为deinit调用未转发到CustomObject,因为Error只是一个协议而且它不知道底层类。虽然,MyError是。我们可以等待其他人澄清这种行为。

为了简单起见,我在这里使用了Playground。看到我甚至没有尝试打印错误值。

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()
}

答案 2 :(得分:0)

此错误已在Xcode 9.3中修复。