In my library, I am creating a series of objects efficiently from data. I then want to put them, in the order that they're created, into an array. I could just use Array.append
, but I've measured and this isn't fast enough for my purposes. Also, it needs to be an Array
and not some other type, e.g. ContiguousArray
. So, I'm using Apple Developer Documentation to set up the array efficiently. However, when I do the first subscript assignment, i.e. buffer[0] = MyType(...)
, I get an EXC_BAD_ACCESS
crash. If I first call memset(buffer.baseAddress, 0, count * MemoryLayout<Element>.stride)
though, it won't crash. Here's my code with that memset
call:
let array: [Element] = try Array(unsafeUninitializedCapacity: count) { (buffer, countToAssign) in
memset(buffer.baseAddress, 0, count * MemoryLayout<Element>.stride)
var isAtEnd = false
for i in 0..<count {
do {
buffer[i] = try decoder.unbox(currentValue, as: Element.self)
JNTDocumentNextArrayElement(currentValue, &isAtEnd)
} catch {
countToAssign = i
throw error
}
}
}
This seems to work, and is substantially faster. As for why it crashes without the memset
call, @Lantua has pointed out in the documentation for UnsafeMutablePointer
that it says "Do not assign an instance of a nontrivial type through the subscript to uninitialized memory. Instead, use an initializing method, such as initialize(to:count:)
.". Is the same true of UnsafeMutable*Buffer*Pointer
? If so, is memset
enough to be safe? And why does this crash occur in the first place?
FYI, here's another example of it initialized from a Sequence
. However, this is also too slow for my purposes:
let array: [Element] = try Array(unsafeUninitializedCapacity: count) { (buffer, countToAssign) in
let sequence: ElementSequence<Element> = ElementSequence(decoder: decoder, count: count, value: currentValue)
let _ = buffer.initialize(from: sequence)
countToAssign = sequence.i
if let error = sequence.error {
throw error
}