2

我目前正在尝试DynamicArray在 Swift 中实现我自己的数据类型。为此,我使用了一些指针。作为我的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。因此,可以创建一个DynamicArray使用init(capacity:)初始化程序的实例,它分配适当的内存量并设置capacity属性。
但后来我也实现了init(count:repeatedValue:)初始化程序,它首先使用init(capacity: count). 然后它将那部分内存中的每个段设置为repeatedValue.

当使用init(count:repeatedValue:)带有数字类型的初始化程序时IntDouble或者Float它工作得很好。然后使用Character, 或者String虽然它崩溃了。虽然它不会一直崩溃,但实际上有时可以通过编译几次来运行,正如可以在此处看到的那样。

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:)描述的方法。而且由于符合它应该可以正常工作。 我现在收到此错误:collectionCollectionType

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

这只是一个误导性的错误信息吗?

4

1 回答 1

2

是的,这可能不会与整数等基本惰性类型崩溃,但会与字符串或数组一起崩溃,因为它们更复杂并且在创建/销毁时为自己分配内存。

它崩溃的原因是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
}

但是,您还需要注意其他一些事情,即这段代码目前不可避免地会泄漏内存。原因是,在销毁结构之前的某个时间点,您需要 destroy内容和dealloc指向的内存DynamicArray,否则您会泄漏。由于您不能deinit在结构中包含一个类,因此无法自动执行此操作(这是假设您不希望数组的用户在超出范围之前自己手动执行此操作)。

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

于 2015-02-27T17:42:22.210 回答