将 UnsafeMutablePointer 与 String 或 Character 类型一起使用时的不一致

Inconsistencies when using UnsafeMutablePointer with String or Character types

我目前正在尝试在 Swift 中实现我自己的 DynamicArray 数据类型。为此,我稍微使用了一些指针。作为我的 root,我使用的是通用类型 UnsafeMutablePointer T:

struct DynamicArray<T> {
    private var root: UnsafeMutablePointer<T> = nil
    private var capacity = 0 {
        didSet {
            //...
        }
    }

    //...

    init(capacity: Int) {
        root = UnsafeMutablePointer<T>.alloc(capacity)
        self.capacity = capacity
    }

    init(count: Int, repeatedValue: T) {
        self.init(capacity: count)

        for index in 0..<count {
            (root + index).memory = repeatedValue
        }
        self.count = count
}

    //...
}

现在如您所见,我还实现了 capacity 属性,它告诉我当前为 root 分配了多少内存。因此,可以使用 init(capacity:) 初始化程序创建 DynamicArray 的实例,它分配适当的内存量,并设置 capacity 属性.
但后来我还实现了 init(count:repeatedValue:) 初始化程序,它首先使用 init(capacity: count) 分配所需的内存。然后它将那部分内存中的每个段设置为 repeatedValue.

当使用 init(count:repeatedValue:) 初始化器和 IntDoubleFloat 等数字类型时,它工作得很好。然后使用 CharacterString 尽管它崩溃了。虽然它不会一直崩溃,但有时确实可以工作,正如可以看到的那样 here,通过编译几次。

var a = DynamicArray<Character>(count: 5, repeatedValue: "A")
println(a.description) //prints [A, A, A, A, A]
//crashes most of the time

var b = DynamicArray<Int>(count: 5, repeatedValue: 1)
println(a.description) //prints [1, 1, 1, 1, 1]
//works consistently

为什么会这样?是否与 StringCharacter 持有不同长度的值有关?


更新 #1:

现在@AirspeedVelocity 用 init(count:repeatedValue:) 解决了这个问题。 DynamicArray 包含另一个初始化程序,它最初以与 init(count:repeatedValue:) 类似的方式工作。我将其更改为可以工作,正如@AirspeedVelocity 为 init(count:repeatedValue:) 所描述的那样:

init<C: CollectionType where C.Generator.Element == T, C.Index.Distance == Int>(collection: C) {
    let collectionCount = countElements(collection)
    self.init(capacity: collectionCount)
    
    root.initializeFrom(collection)
    count = collectionCount
}

我正在使用 initializeFrom(source:) 方法,如 here 所述。因为 collection 符合 CollectionType 它应该可以正常工作。
不过,我现在收到此错误:

<stdin>:144:29: error: missing argument for parameter 'count' in call
    root.initializeFrom(collection)
                        ^

这又是误导性的错误信息吗?

是的,很可能这不会与整数等基本惰性类型崩溃,但会与字符串或数组崩溃,因为它们更复杂并且会在 creation/destruction.

上为自己分配内存

崩溃的原因是 UnsafeMutablePointer 内存在使用前需要初始化(同样,在释放前需要用 destroy 取消初始化)。

所以不要分配给 memory 属性,你应该使用 initialize 方法:

for index in 0..<count {
    (root + index).initialize(repeatedValue)
}

由于从另一个值集合进行初始化非常普遍,因此 initialize 的另一个版本采用一个值。您可以将它与另一个辅助结构 Repeat 结合使用,后者是重复多次的相同值的集合:

init(count: Int, repeatedValue: T) {
    self.init(capacity: count)
    root.initializeFrom(Repeat(count: count, repeatedValue: repeatedValue))
    self.count = count
}

但是,还有一点您需要注意,即这段代码目前不可避免地会泄漏内存。原因是,在销毁 DynamicArray 结构之前,您需要 destroy 内容和 dealloc 指向的内存,否则会泄漏。因为你不能在结构中有一个 deinit,只有一个 class,这不可能自动完成(这是假设你不希望你的数组的用户自己这样做在超出范围之前手动操作)。

此外,如果您想通过写时复制实现值语义(与 ArrayString 一样),您还需要一种方法来检测您的内部缓冲区是否被多次引用。查看 ManagedBufferPointer 以查看 class 为您处理此问题。