Swift 使用 Struct 的 UnsafeMutablePointer 获取字符串错误的值

Posted

技术标签:

【中文标题】Swift 使用 Struct 的 UnsafeMutablePointer 获取字符串错误的值【英文标题】:Swift Use UnsafeMutablePointer of Struct to get value of String Error 【发布时间】:2017-06-07 16:19:56 【问题描述】:

我想获取结构的指针,并获取或设置值。代码在这里

protocol TestProtocol 



struct TestStruct: TestProtocol 
    var username: String?


class TestViewModel 
    var testStruct: TestProtocol?

    func test() -> UnsafeMutablePointer<TestStruct?>? 
        guard let _ = self.testStruct as? TestStruct else 
            return nil
        
        return withUnsafeMutablePointer(to: &(self.testStruct))  (pointer) -> UnsafeMutablePointer<TestStruct?> in
            return pointer.withMemoryRebound(to: TestStruct?.self, capacity: MemoryLayout<TestStruct?>.size)  (point) -> UnsafeMutablePointer<TestStruct?> in
                return point
            
        
    


func getvalue() 
     let testViewModel = TestViewModel()
     var testStruct = TestStruct()
     testStruct.username = "123"
     testViewModel.testStruct = testStruct
     print("\(testViewModel.test()?.pointee?.username)")
     // here print Optional(""), why not "123"?

get value 函数打印结果是错误的,但是如果 TestStruct 只有一个 Int 的 var,结果是正确的。就这样:

struct TestStruct: TestProtocol 
        var number: Int?
    

func getvalue() 
         let testViewModel = TestViewModel()
         var testStruct = TestStruct()
         testStruct. number = 123
         testViewModel.testStruct = testStruct
         print("\(testViewModel.test()?.pointee?.username)")
         // Optional(123)
    

为什么?如何解决?

【问题讨论】:

这都是未定义的行为——传递给 withUnsafeMutablePointer(to:).withMemoryRebound(to:) 的闭包中的指针参数仅在这些调用的生命周期内有效。还直接将TestProtocol? 实例的内存重新解释为TestStruct? 注定要失败(它可能在某些情况下似乎“工作”,例如在您使用Int 的情况下 -但这实际上只是基于实现细节和仍未定义的行为)。您要在这里解决的实际问题是什么? @Hamish 在 TestViewModel 类中有一个 TestProtocol? 的 var testStruct,但是我将一个 TestStruct 实例 testStruct 传递给它,它是值引用,我想将它转换为 TestStruct 并将值设置为自身,而不是it 的复制值。你能明白吗?我的英语是台球 @Hamish 你能帮我吗?谢谢!! 对不起,但我仍然不明白你要解决的确切问题——如果你想要引用语义,你应该使用一个类,甚至可能是一个闭包(例如,参见 @987654321 @)。 【参考方案1】:

这是使用指向对象的指针制作/使用/完成的模式:

// this allocates memory for the pointer
let somePointer = UnsafeMutablePointer<someType>.allocate(capacity: 1)
// this initializes the memory to the value
somePointer.initialize(to: someInstanceOfSomeTime)
// this allows me to access the object
somePointer.pointee.someMethod(/* ... */)
// this destroys the memory
somePointer.deinitialize(count: 1)
somePointer.deallocate(capacity: 1)

由于您的内存可能包含直接或间接包含引用计数类型的类型,因此了解指针具有状态并且您需要管理所有这些状态的生命周期至关重要。

这是你典型的一生 - 分配了一个指针,但其内容未初始化,不得使用 - 一个指针被初始化并且它的内容现在可以被使用 - 一个指针被取消初始化,现在它的内容可能不再被使用,但你可以再次初始化它 - 一个指针被释放,可能不再被初始化。

了解这些步骤非常重要。当您使用典型的 swift 代码时,您正在执行所有这些步骤,但它们是隐式的。举个简单的例子:

public func printThingValue(t:Thing) 
    let aCopy = t.value
    print(aCopy)

让我们考虑 aCopy 的生命周期。在调用之前,aCopy 没有被分配。当您进入函数时,aCopy 被分配(在堆栈上)但未初始化。当分配发生时, aCopy 被初始化。调用 print 时,使用 aCopy。在printThingValue 退出之前,aCopy 被取消初始化。当它退出时,aCopy 被释放(堆栈帧被破坏)。

知道这个生命周期非常重要,因为这正是指针使用的模型,但它不再是自动/隐式的。

与此相关的是问题的答案:我可以构造一个未初始化的值类型吗?答案是肯定的——有一种方法,那就是通过UnsafeMutablePointer。如果您调用allocate,您将获得代表您的值类型的未初始化内存,但这非常危险。如果您在初始化之前分配给pointee,您需要快速编译器注入将破坏那里的值的代码。如果指向的类型包含引用类型,这将崩溃或导致未定义的行为。

了解和管理这些步骤是 Apple 试图让您可以使用指针的方式,但它会带来一系列新的危险:

- using an unallocated pointer (crash)
- using/assigning to an allocated but uninitialized pointer (crash)
- failing to deinitialize an initialized pointer (reference leak)
- failing to deallocate a deinitialized pointer (memory leak)

【讨论】:

以上是关于Swift 使用 Struct 的 UnsafeMutablePointer 获取字符串错误的值的主要内容,如果未能解决你的问题,请参考以下文章

Swift 使用 Struct 的 UnsafeMutablePointer 获取字符串错误的值

SWIFT:将字节从 NSData 粘贴到 Struct?

在 Swift 中使用 const char * 初始化 C-struct

Swift - 将struct数组传递给另一个struct的init

Swift结构体(Struct)

Swift 如何访问 Struct 对象字典中的特定变量