【问题标题】:dealloc in SwiftSwift中的dealloc
【发布时间】:2014-10-19 07:30:41
【问题描述】:

我想在视图控制器生命周期结束时进行一些清理,即删除NSNotificationCenter 通知。实现dealloc 会导致 Swift 编译器错误:

Cannot override 'dealloc' which has been marked unavailable

在 Swift 中,在对象生命周期结束时执行一些清理的首选方法是什么?

【问题讨论】:

    标签: swift dealloc


    【解决方案1】:
    deinit {
        // perform the deinitialization
    }
    

    来自Swift Documentation

    在类实例被调用之前立即调用反初始化器 解除分配。您使用 deinit 关键字编写反初始化器,类似 了解如何使用 init 关键字编写初始化程序。反初始化器 仅适用于类类型。

    通常情况下,您不需要执行手动清理 实例被释放。但是,当您使用自己的 资源,您可能需要执行一些额外的清理 你自己。例如,如果您创建一个自定义类来打开一个文件并 向其中写入一些数据,您可能需要在 类实例被释放。

    【讨论】:

      【解决方案2】:
      deinit {
          // perform the deinitialization
      }
      

      是 Swift "dealloc" 的正确答案。

      不过,最好指出 iOS 9 中的新功能 NSNotificationCenter 不再需要清理!

      https://developer.apple.com/library/content/releasenotes/Foundation/RN-FoundationOlderNotes/index.html#X10_11Notes

      NSNotificationCenter

      在 OS X 10.11 和 iOS 9.0 中,NSNotificationCenter 和 NSDistributedNotificationCenter 将不再向可能被释放的已注册观察者发送通知。如果观察者能够被存储为清零弱引用,则底层存储将观察者存储为清零弱引用,或者如果对象不能被弱存储(即它有一个自定义保留/释放机制会阻止运行时从能够弱存储对象)它将对象存储为非弱归零引用。这意味着观察者不需要在他们的释放方法中取消注册。将路由到该观察者的下一个通知将检测归零的引用并自动取消注册观察者。如果一个对象可以被弱引用,则在释放期间将不再向观察者发送通知;在非弱归零参考观察者的情况下,仍然存在之前在 dealloc 期间接收通知的行为。通过 -[NSNotificationCenter addObserverForName:object:queue:usingBlock] 方法的基于块的观察者在不再使用时仍然需要取消注册,因为系统仍然持有对这些观察者的强引用。仍然支持过早地移除观察者(弱引用或归零引用)。 CFNotificationCenterAddObserver 不符合这种行为,因为观察者可能不是对象。

      但请注意以下关于强引用的几点,因此您可能不得不担心清理...?

      【讨论】:

      • 除非通知块有强引用,否则必须移除观察者。
      • +1 无需清理观察者。重要的是要知道!我使所有捕获引用变得弱,所以永远不必处理这个。
      • 根据文档,通知块似乎总是被强烈引用。所以:如果您使用块来处理通知,您必须在 deinit 中取消注册。
      【解决方案3】:

      https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Deinitialization.html

      Swift 会在不再需要时自动释放您的实例,以释放资源。 Swift 通过自动引用计数 (ARC) 处理实例的内存管理,如自动引用计数中所述。通常,当您的实例被解除分配时,您不需要执行手动清理。但是,当您使用自己的资源时,您可能需要自己执行一些额外的清理工作。例如,如果您创建一个自定义类来打开一个文件并向其中写入一些数据,您可能需要在释放类实例之前关闭该文件。

      类定义每个类最多可以有一个反初始化器。反初始化器不带任何参数,写成不带括号:

      deinit {
          // perform the deinitialization
      }
      

      【讨论】:

        【解决方案4】:

        在释放之前需要移除观察者,否则会发生崩溃。可以使用

        deinit {
            // perform the deinitialization
            print("deinit")
        
            removeObserver(self, forKeyPath: kSelectedViewControllerKey, context: nil)
            removeObserver(self, forKeyPath: kSelectedIndexKey, context: nil)
        
        }
        

        【讨论】:

          【解决方案5】:

          如果您真的想在视图超出范围时删除某些内容并在范围内开始,我建议让我们在控制器的 didAppear 和 didDisappear 方法中执行此操作。甚至 NSNotificationCenter 也可以在视图出现/消失时移除并添加回来。

          【讨论】:

            【解决方案6】:

            deinit 调用其他类中的方法时要小心,它可能会崩溃

            【讨论】:

            • 投反对票,因为不一定如此。来自ref. docs因为一个实例直到调用它的 deinitializer 之后才被释放,所以 deinitializer 可以访问它被调用的实例的所有属性,并且可以根据这些属性修改它的行为(例如查找名称需要关闭的文件)。
            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-10-08
            • 1970-01-01
            • 1970-01-01
            • 2017-01-28
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多