c - Swift中的字节数组指针范围
问题描述
我在需要指向uint8_t
缓冲区的指针的库中调用 C 函数。我试图在 Swift 中引用 UInt8 数组,如下所示(稍后传递给 C 函数):
import Foundation
struct CStruct {
let val: UnsafeMutablePointer<UInt8>
let count: UInt32
}
var structs = [CStruct]()
var datas = [[UInt8]]()
for _ in 0..<10 {
// In the real application, Data contains data rather than being zero length
let d = Data()
var data = [UInt8](d)
// I don't want the `data` buffers to be deallocated, so I store them in an array.
datas.append(data)
structs.append(CStruct(val: &data, count: UInt32(data.count)))
}
print("Structs: \(structs)")
// pass the structs to a C function which processes them
这给出了警告
Inout 表达式创建一个临时指针,但参数 'val' 应该是一个比对 'init(val:count:)' 的调用更长的指针
我不能使用data.withUnsafeMutableBufferPointer
它提供的指针,因为它超出了闭包的范围。我不能使用Unmanaged
它,因为它只对类起作用,并且Array
是一个结构。我也不确定将data
数组添加到父作用域中的数组是否会延长其生命周期(因为它是一个结构)。实现这一目标的正确方法是什么?
编辑:
我的解决方案,基于@bscothern 的回答:
import Foundation
struct CStruct {
let val: UnsafeMutablePointer<UInt8>
let count: UInt32
}
var structs = [CStruct]()
var dataStorage: [UnsafeMutablePointer<UInt8>] = []
for _ in 0..<10 {
// In the real application, Data contains data rather than being zero length
let d = Data()
let ptr = UnsafeMutablePointer<UInt8>.allocate(capacity: d.count)
d.withUnsafeBytes { (buff) -> Void in
ptr.initialize(from: buff.bindMemory(to: UInt8.self).baseAddress!, count: d.count)
}
dataStorage.append(ptr)
structs.append(CStruct(val: ptr, count: UInt32(d.count)))
}
defer {
for ptr in dataStorage {
ptr.deallocate()
}
dataStorage = []
}
print("Structs: \(structs)")
// pass the structs to a C function which processes them
也许有更好的方法来复制Data
?
解决方案
有很多方法可以解决这个问题,但根本原因是你需要分配一个缓冲区并将其传入。然后你需要有一种方法来知道你什么时候清理它,这样你就不会泄漏内存.
解决这个问题的一个相当简单的方法是使用类类型来管理CStruct
实例的生命周期。这是一个使用类包装器使大部分内存管理自动化的方法示例。
import Foundation
struct CStruct {
let val: UnsafeMutablePointer<UInt8>
let count: UInt32
}
final class CStructLifetimeManager {
private(set) var cStruct: CStruct // This needs to be mutable so it can be deallocated later on.
private init(count: Int) {
var val = UnsafeMutablePointer<UInt8>.allocate(capacity: count)
for i in 0..<10 {
val[I] = 0
}
self.cStruct = CStruct(val: val, count: UInt32(count))
}
deinit {
cStruct.val.deinitialize(count: 10) // Not needed if you really just have UInt8 types since they are trivial
cStruct.val.deallocate()
}
static func createStruct(count: Int) -> CStructLifetimeManager {
CStructLifetimeManager(count: count)
}
}
var structs = [CStruct]()
var structLifetimeManagers = [CStructLifetimeManager]() // This now keeps the manager objects alive
for _ in 0..<10 {
let cStructLifetimeManager = CStructLifetimeManager(count: 10)
structs.append(cStructLifetimeManager.cStruct)
structLifetimeManagers.append(cStructLifetimeManager)
}
print("Structs: \(structs)")
这种方法的问题是structs
镜像structLifetimeManagers
相同的CStruct
实例,但它们仍然不同。这意味着它可以停止存在于遗嘱中structLifetimeManagers
,structs
然后访问无效数据。
因此,最好删除第二个数组,让管理生命周期的包装器对象成为您使用的对象。而且您只CStruct
在需要时才进入。如果这不可能,您只需要非常小心,以确保事情不会超出范围。
推荐阅读
- reactjs - 将 UI 组件双向绑定到可观察对象以减少渲染的最佳方法是什么?
- flutter - Flutter:如何将容器的高度调整为其连续兄弟的最大高度
- python - python中的'if'和'if not',循环搜索文本文件中的特定子字符串。两者有什么区别?什么时候用哪个?
- types - 我如何让 Agda 的宇宙检查器相信我所做的事情是有根据的?
- javascript - Enter 键的行为类似于 JavaScript 中的箭头键
- amazon-web-services - 将 Elasticache 与 RDS 连接起来?
- vue.js - 如何在 vuex 中使用 InfiniteLoading(错误)
- c# - 在 linq where 子句中强制转换属性
- python - Python:Portaudio Errno -9986 内部 PortAudio 错误
- angular - 在子组件和父组件之间传递数据