代码之家  ›  专栏  ›  技术社区  ›  Nick

Swift中的字节数组指针作用域

  •  0
  • Nick  · 技术社区  · 4 年前

    我正在调用库中的一个C函数,它需要指向 uint8_t

    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 数组到父作用域中的数组将延长其生存期(因为它是一个结构)。正确的方法是什么?


    编辑:

    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 ?

    1 回复  |  直到 4 年前
        1
  •  1
  •   bscothern    4 年前

    有很多方法可以解决这个问题,但根本原因是需要分配缓冲区并将其传入。然后你需要有一个方法知道你什么时候清理它,这样你就不会泄漏内存。

    解决这个问题的一个相当简单的方法是使用类类型来管理 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 照搬 结构 结构生命时间管理器 一个 结构

    所以最好删除第二个数组,让包装器对象管理生命周期。你只需要 结构 需要的时候。如果这是不可能的,你只需要非常小心,以确保事情没有超出范围之前,他们应该。