从 NSObject 派生时,Swift iOS 内存泄漏指示 XCode8

Posted

技术标签:

【中文标题】从 NSObject 派生时,Swift iOS 内存泄漏指示 XCode8【英文标题】:Swift iOS Memory Leak Indicated XCode8 when Derived from NSObject 【发布时间】:2016-10-14 13:44:10 【问题描述】:

Xcode8 显示仪器和内存图存在内存泄漏。我已将其缩小到这一点:从 NSObject 派生会产生泄漏指示。我不知道为什么。

我需要一个 NSObject 以便稍后使用 @objc 指令。

存储在 mDict Dictionary 中的 Test 实例在 Xcode 中被指示为泄漏。

这是作为 ios Single-View-Application 项目在运行 iOS10.0 的 iPhone5s 模拟器中运行的

import Foundation

class Test: NSObject  // <-- derived from NSObject produces leak indication below

    static var cTest: Test! = nil
    var mDict: [String : Test] = Dictionary<String, Test>()

    static func test() -> Void 
        cTest = Test()
        cTest.mDict["test"] = Test() // <-- alleged leak
    


class Test  // <-- NOT derived from NSObject, NO leak indication

    static var cTest: Test! = nil
    var mDict: [String : Test] = Dictionary<String, Test>()

    static func test() -> Void 
        cTest = Test()
        cTest.mDict["test"] = Test() // <-- NO leak
    


// from AppDelegate didFinishLaunchingWithOptions
// ...
    Test.test()
// ...

【问题讨论】:

尝试在 NSObject 示例中将 mDict 声明为 weak var。我怀疑在使用后没有释放 mDict 的情况下发生了一个强大的内存循环。 谢谢@Danoram。当我尝试得到“弱”时,可能仅适用于类和类绑定协议类型,而不是“字典”。另外,我希望 mDict 坚持下去,即不弱,但值得尝试收集线索。有趣的是,当显示泄漏时,我可以稍后将 cTest 设置为 nil 并且所有内容都被解除分配。什么都没有泄露。嗯... 令人着迷。就解决泄漏而言,这可能是您的最佳选择 【参考方案1】:

swift 中的内存管理与类的目标 c 有点不同。比起class Test: NSObject,你可能会错过这个特性。 如果您尝试class Test: AnyObject,您必须解决您的问题。

或者尝试使用deinit 在您使用它的每一刻

class Test: NSObject  // <-- using deinit

   static var cTest: Test! = nil
   var mDict: [String : Test] = Dictionary<String, Test>()

   static func test() -> Void 
      cTest = Test()
      cTest.mDict["test"] = Test() // <-- alleged leak
   

   deinit 

   

但最佳实践将其声明为 weak 变量,例如 weak var test: Test? 在您的情况下

【讨论】:

谢谢@Achron。是的。泄漏指示器不见了但是我需要从 NSObject 继承才能使用@@objc。当您从 AnyObject 继承时,这是不可能的。其他想法/解决方案? 也许如果你在对象上使用deinit,你会解决的。如果仍然无法正常工作,请尝试使用init 谢谢@Achron。不幸的是 init() 和 deist 没有帮助。在我的示例中, deinit 永远不会被调用。泄漏仍然存在。 刚刚将这一行添加到答案中。尝试将变量声明为weak 谢谢@Achron。你的意思是 var mDict 作为弱 var mDict,对吧?之前的一张海报也提到了这一点。不幸的是,它会产生编译器错误。 (见上文。)如果理解正确,请告诉我,如果理解正确,正确的代码语法是什么?【参考方案2】:

这似乎已在 Xcode 发行版 8.2 (8C38) 中得到修复。不再显示泄漏。

【讨论】:

其实我还是发现了一个问题。【参考方案3】:

这与原始问题基本相同。这是我有类似问题的测试代码。 YMMV。在 iPad air 上运行 iOS 10.1 版本 8.2 (8C38)

import UIKit

class Thing 

class Test: NSObject

    static let shared = Test()
    var dictionary: [String: Thing] = [:]

    func method() 
        dictionary = ["value": Thing()]
    


class ViewController: UIViewController 
    override func viewDidLoad() 
        super.viewDidLoad()
        Test.shared.method()
        print("Leaky leaky... click on the memory visualizer to see issues.")
    

如果您删除 NSObject,问题就会消失。我为它的价值提交了一份雷达。

如果我作为命令行工具运行并使用命令行leaks 命令进行检查,我看不到问题。

【讨论】:

以上是关于从 NSObject 派生时,Swift iOS 内存泄漏指示 XCode8的主要内容,如果未能解决你的问题,请参考以下文章

没有在 NSObject 类 ios swift 中获取委托方法的回调

IOS - 啥是 Swift 字符类型的 ObjC NSObject 等价物?

从 NSObject 类更新 UITabBarController 栏项目

派生时的自动类装饰(或验证)

Swift Json - 从 API 获取数据

我的 swift 函数的返回类型必须继承自 NSObject