【问题标题】:When constructing a Swift UnsafeMutablePointer manually, is destroy/dealloc obligatory after alloc?手动构建 Swift UnsafeMutablePointer 时,alloc 后是否必须强制销毁/释放?
【发布时间】:2015-01-04 17:29:46
【问题描述】:

假设在 Swift 中我手动构造了一个 C 数组并传递它,如下所示:

override func drawRect(rect: CGRect) {
    let c = UIGraphicsGetCurrentContext()
    var arr = UnsafeMutablePointer<CGPoint>.alloc(4)
    arr[0] = CGPoint(x:0,y:0)
    arr[1] = CGPoint(x:50,y:50)
    arr[2] = CGPoint(x:50,y:50)
    arr[3] = CGPoint(x:0,y:100)
    CGContextStrokeLineSegments(c, arr, 4)
}

(我知道我没有这样做,但请耐心等待。)如果我不在这个 UnsafeMutablePointer 上调用 destroy 和/或 dealloc,我是吗?泄露四个 CGPoints 的内存?

【问题讨论】:

  • 不能用 XCode 检查内存泄漏吗?
  • 如果你不调用arr.dealloc(4),你会泄漏内存。这似乎是一个很好的概述(可能是重复的?):stackoverflow.com/questions/27670643/…
  • @doctordoder 你试过了吗?这里没有 CGPoint 对象——它只是某处的一块内存,所以我不能轻易地从所有内存块中挑选出来。
  • @MartinR 谢谢,我会重写以在问题中包含dealloc;我并不是要限制我的义务可能是什么。但是,这个问题是我开始的地方(请注意我在回答后的评论/问题);它并没有明确告诉我我想知道什么。
  • 这个 alloc/initialize/destroy/dealloc 东西的文档记录极差。也许您在开发者论坛上的帖子会给出一些结果。据我了解,destroy() 调用对象的 deinit 方法,这是 CGPoint 的无操作,因此您可以安全地省略它。 dealloc() 是 alloc() 的对应物(如在 malloc/free 中),所以没有它你会泄漏内存。

标签: swift memory-management


【解决方案1】:

UnsafeMutablePointer 的文档非常清楚:

///                      This type provides no automated
/// memory management, and therefore the user must take care to allocate
/// and free memory appropriately.

因此,如果您分配但不取消分配,则会泄漏内存。销毁指针对象时不会自动释放。

关于是否应该在释放之前销毁,也很清楚:

/// The pointer can be in one of the following states:
///
/// - memory is not allocated (for example, pointer is null, or memory has
/// been deallocated previously);
///
/// - memory is allocated, but value has not been initialized;
///
/// - memory is allocated and value is initialized.

但请记住,您可以在这些状态之间来回转换。因此,在分配然后初始化然后取消初始化(也就是销毁)对象之后,内存不再处于“初始化”状态,因此您可以重新初始化或取消分配它。您也可以先分配,然后再解除分配,而无需初始化。

当调用dealloc:

/// Deallocate `num` objects.
...
/// Precondition: the memory is not initialized.
///
/// Postcondition: the memory has been deallocated.

因此,您必须在调用dealloc 之前对任何已初始化的对象调用destroy。你可能是对的,因为像 CGPoint 这样的东西是完全惰性的(只是两个浮点数的结构)它可能在你调用 @ 之前不调用 destroy 没有任何害处987654329@ 但你不能确定不知道实现(指针结构和编译器可能,因为标准库是语言的准部分,可能会有一些优化),一般来说,它是只是不是一个好习惯。你迟早会忘记销毁String,然后你会后悔的。

(这些都不能说明move 操作顺便说一句,它结合了初始化新内存和销毁旧内存)

如果您希望通过UnsafePointer 进行某种自动的内存自我清理,我认为这是不可能的,因为 a) 它是一个结构,所以不能实现 deinit 来自动-超出范围时释放,并且 b) 它不跟踪它自己的大小 - 您必须跟踪您分配了多少,并在对 deallocate 的调用中明确提供。

标准库中有一些东西可以自动释放内存,而您不必自己动手——HeapBufferStorage,标准库中唯一的一个类。大概它是一个专门从deinit 的实现中受益的类。还有HeapBuffer 来管理它,它有一个方便的isUniquelyReferenced() 函数,可以让你判断它是否被复制(即使它是一个结构),因此可以让你实现类似于写时复制的功能数组和字符串。

【讨论】:

  • 当使用 Martin R 的重复计时器的想法来重绘我的视图时,无论我是否调用 destroydealloc,我都会看到内存使用量稳步上升。因此,我不相信这会有所作为。
  • 请记住,使用 dealloc 将内存还给运行时并不意味着运行时会将其直接还给操作系统...
【解决方案2】:

在 Swift 2 中,通过将 alloc/dealloc 与新的 defer 关键字配对,可以使 UnsafeMutablePointer 变得不那么令人沮丧:

let ptr = UnsafeMutablePointer<T>.alloc(1)
defer { ptr.dealloc(1) }

【讨论】:

  • 如果我在课堂上而不是在 fun 中声明一个 var 怎么办?我应该实现deinit 函数并调用destroy 然后dealloc 吗?
【解决方案3】:

这适用于像我这样通过搜索 CGContextStrokeLineSegments 找到此问题/答案线程的其他人。如果您的目的是在 Swift 中调用此函数,则不必构造 C 数组指针,即使该函数的第二个参数是 UnsafeMutablePointer。你可以直接将一个 Swift 数组传递给它,甚至是一个不可变的数组,而不是一个带有分配和释放的 C 指针。例如:

override func drawRect(rect: CGRect) {
   if let c = UIGraphicsGetCurrentContext()
   {
       let arr = [CGPoint(x:0,y:0), CGPoint(x:50,y:50), CGPoint(x:50,y:50), CGPoint(x:0,y:100)]
       CGContextStrokeLineSegments(c, arr, 4)
   }
}

【讨论】:

  • 问题规定:“我知道我不必这样做,但请耐心等待”,所以你跑题了
猜你喜欢
  • 2015-09-06
  • 2012-12-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-07-19
  • 2023-01-27
  • 1970-01-01
相关资源
最近更新 更多