我对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错误(在这种情况下,我会为它开一个问题),但是我也错过了一个关键部分理解。如果我所做的是不好的做法,我想了解原因。
答案 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
类型造成的。
实际上,如果您使用Error
或MyError
作为数组类型,则可以使用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中修复。