【问题标题】:Swift Memory Deinit During Reassignment重新分配期间的 Swift 内存 Deinit
【发布时间】:2026-01-02 11:05:01
【问题描述】:

假设我有一个自定义的 CIImage 类。

import UIKit

class MyCIImage: CIImage {

    var VC:RealtimeDepthMaskViewController!

    deinit { print("Deinit") }

    override init(cvPixelBuffer pixelBuffer: CVPixelBuffer) {
        super.init(cvPixelBuffer: pixelBuffer)
    }

    override init(cvPixelBuffer pixelBuffer: CVPixelBuffer, options: [CIImageOption : Any]? = nil) {
        super.init(cvPixelBuffer: pixelBuffer, options: options)
    }

    convenience init(cvPixelBuffer pixelBuffer: CVPixelBuffer, VC:RealtimeDepthMaskViewController) {
        self.init(cvPixelBuffer: pixelBuffer)
        self.VC = VC
        print("Init")
    }

    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }

然后我们创建并分配一个变量。

let image:CIImage = MyCIImage(cvPixelBuffer: somePixelBuffer) //"Init"

然后,我想重新分配变量

image = MyCIImage(cvPixelBuffer: someOtherPixelBuffer) //"Init" "Deinit"

执行这 2 个命令我会得到的打印输出是

"Init"
"Init"
"Deinit"

为什么内存完全被取消初始化而不是重新分配?内存地址不会保持不变并且内存地址所在的项目不会改变吗?如果不是,这可能是一种情况吗?这样内存@地址image就永远不为零,只是不断变化?

【问题讨论】:

  • 每次初始化一个类时,都会分配新的内存。如果要重用具有不同数据的对象,则必须在不调用 init(..) 的情况下分配图像
  • 有趣。我将使用这些信息。谢谢
  • 首先,我假设它是var image = ...,而不是let(因为您不能替换常量let 引用)。其次,假设它是var,您已经用一个新实例替换了image 引用的对象,从而丢弃了对先前实例的引用(因此第一个实例的deinit)。
  • 无关,但图片对RealtimeDepthMaskViewController的引用令人担忧。图像对象没有业务保持对视图控制器的引用。如果这真的是您尝试实现的委托模式,您通常会使用协议(以保持它们松散耦合)并使引用变弱(以避免强引用循环)。
  • 哎呀——那是为了测试保留周期,如果有对 VC 的强烈引用,请尝试确定它为什么在内存中。我知道这是不好的模式 - 更多的是研究。它不是出于实际目的。它是一个 var,我确实在 SO 上编写了测试。

标签: swift memory automatic-ref-counting ciimage


【解决方案1】:

当你这样做时......

var image: CIImage = MyCIImage(cvPixelBuffer: somePixelBuffer)

...这将导致您的第一个“Init”日志记录语句。

当你这样做时......

image = MyCIImage(cvPixelBuffer: someOtherPixelBuffer)

...这将做两件事:

  1. 您正在创建一个新的MyCIImage 实例,这会导致另一个与此新实例关联的“Init”日志消息。

  2. 但是,因为您将旧的 MyCIImage 的强引用 image 替换为新的引用,所以您正在删除对第一个实例的强引用。而且,由于第一个实例没有剩余的强引用,它将被释放,您将看到与其关联的“Deinit”日志消息。

你问:

为什么内存完全被取消初始化而不是重新分配?内存地址不会保持不变,内存地址所在的项目只是改变吗?

不。当您创建一个新实例(使用MyCIImage(...) 语法)时,它会创建一个具有新地址的新对象。这就是对象和自动引用计数的工作原理。

如果不是,这是一种可能的情况吗?这样memory@address图像永远不会为零,只是不断变化?

阻止它获取新地址的唯一方法是避免创建您的类型的新实例(即,如果您只使用该原始实例并避免创建新实例)。

但是如果它是一个新图像,我们只是为它创建一个新实例,而不用担心它是一个新对象这一事实。您的父类CIImage 的许多属性(例如pixelBuffercgImage 等)都是只读的,不能在后台换出或更改。您的父类CIImage 确实适用于单一、独特的图像。作为一般的设计原则,这是一件非常好的事情,减少了无意共享可能引起的各种问题。在这种情况下,CIImage 就是这样设计的。

【讨论】:

  • 我得出了这个结论。现在的目标是如何适当地执行我的设计模式以实现我的目标。谢谢!