将 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:)
初始化器和 Int
、Double
或 Float
等数字类型时,它工作得很好。然后使用 Character
或 String
尽管它崩溃了。虽然它不会一直崩溃,但有时确实可以工作,正如可以看到的那样 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
为什么会这样?是否与 String
和 Character
持有不同长度的值有关?
更新 #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,这不可能自动完成(这是假设你不希望你的数组的用户自己这样做在超出范围之前手动操作)。
此外,如果您想通过写时复制实现值语义(与 Array
和 String
一样),您还需要一种方法来检测您的内部缓冲区是否被多次引用。查看 ManagedBufferPointer
以查看 class 为您处理此问题。
我目前正在尝试在 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:)
初始化器和 Int
、Double
或 Float
等数字类型时,它工作得很好。然后使用 Character
或 String
尽管它崩溃了。虽然它不会一直崩溃,但有时确实可以工作,正如可以看到的那样 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
为什么会这样?是否与 String
和 Character
持有不同长度的值有关?
更新 #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,这不可能自动完成(这是假设你不希望你的数组的用户自己这样做在超出范围之前手动操作)。
此外,如果您想通过写时复制实现值语义(与 Array
和 String
一样),您还需要一种方法来检测您的内部缓冲区是否被多次引用。查看 ManagedBufferPointer
以查看 class 为您处理此问题。