声明一个指向属性的指针并将其作为 inout 参数传递给 Swift 中的 func?

我需要选择一些属性之一并通过引用传递它以将其设置在 func 中。大概代码:

jobs

问题是 var someProperty = [Someclass]() var someProperty2 = [Someclass]() func someFunc(someObject: inout [Someclass]) { ... someObject = ... // } //usage code let obj: [Someclass] if someCase { obj = self.someProperty ... } else { obj = self.someProperty2 ... } someFunc(&obj) 不能用作 obj 参数但即使我将其声明为 inout 然后 var 已更改但不是 objsomeProperty

我读到我需要将 someProperty2 声明为 obj 并且有类似的问题。但我不知道如何将它们应用于上面的代码以仅修复这 3 行(而不更改其余代码):

UnsafeMutablePointer

如何解决这个问题?

附言换句话说,我需要类似 let obj: [Someclass] obj = self.someProperty obj = self.someProperty2 的东西,但 Swift 不允许

tiancai61 回答:声明一个指向属性的指针并将其作为 inout 参数传递给 Swift 中的 func?

在评论中讨论后,问题旨在具体说明如何与 UnsafeMutablePointer 合作以实现结果,而不是关于实现结果的最佳方式。

重要的是,从获取指针到使用指针的整个代码都在 withUnsafeMutablePointer 范围内。因为它归结为在两个数组之间进行选择,然后通过别名将其中一个传递给 someFunc,所以您不知道哪个指针必须保持活动状态,因此您必须保持它们both 直播。否则,当 Swift 使程序无效时,您的程序可能会崩溃。

使用指针达到预期效果的正确且安全的方法是这样的:

func pointerSolution()
{
    var sourceArray1 = [1,2,3,4]
    var sourceArray2 = [5,6]

    func someFunc(_ someArray: inout [Int]) {
        someArray.indices.forEach { someArray[$0] = 0 }
    }
    
    let someCase = true // just for the sake of compiling a concrete case

    //usage code
    withUnsafeMutablePointer(to: &sourceArray1)
    { src1 in
        withUnsafeMutablePointer(to: &sourceArray2)
        { src2 in
            // Substitute appropriate type for `Int`
            let arrayPtr: UnsafeMutablePointer<[Int]>
            if someCase {
                arrayPtr = src1
                // Some additional code that may or may not involve arrayPtr
            } else {
                arrayPtr = src2
                // Some additional code that may or may not involve arrayPtr
            }
            someFunc(&arrayPtr.pointee)
        }
    }

    print("sourceArray1 = \(sourceArray1)")
    print("sourceArray2 = \(sourceArray2)")
}

如果你必须在多个地方这样做,或者只是想清理嵌套 withUnsafeMutablePointer 块的语法膨胀,你可以提供一个辅助函数:

func withUnsafeMutablePointers<T,R>(
    to value1: inout T,and value2: inout T,_ body: (UnsafeMutablePointer<T>,UnsafeMutablePointer<T>) throws -> R) rethrows -> R
{
    try withUnsafeMutablePointer(to: &value1)
    { ptr1 in
        try withUnsafeMutablePointer(to: &value2)
        { ptr2 in
            try body(ptr1,ptr2)
        }
    }
}

然后在你使用它的地方,你有一层嵌套:

func pointerSolution()
{
    var sourceArray1 = [1,6]

    func someFunc(_ someArray: inout [Int]) {
        someArray.indices.forEach { someArray[$0] = 0 }
    }
    
    let someCase = true // just for the sake of compiling a concrete case

    //usage code
    withUnsafeMutablePointers(to: &sourceArray1,and: &sourceArray2)
    { src1,src2 in
        // Substitute appropriate type for `Int`
        let arrayPtr: UnsafeMutablePointer<[Int]>
        if someCase {
            arrayPtr = src1
            // Some additional code that may or may not involve arrayPtr
        } else {
            arrayPtr = src2
            // Some additional code that may or may not involve arrayPtr
        }
        someFunc(&arrayPtr.pointee)
    }

    print("sourceArray1 = \(sourceArray1)")
    print("sourceArray2 = \(sourceArray2)")
}

如果你想危险地生活,你可以这样做:

func dangerousPointerSolution()
{
    var sourceArray1 = [1,6]

    func someFunc(_ someArray: inout [Int]) {
        someArray.indices.forEach { someArray[$0] = 0 }
    }

    let someCase = true // just for the sake of compiling a concrete case

    //usage code
    let address: Int
    if someCase {
        address = withUnsafeMutablePointer(to: &sourceArray1) { Int(bitPattern: $0) }
        // Some additional code that may or may not involve address
    } else {
        address = withUnsafeMutablePointer(to: &sourceArray2) { Int(bitPattern: $0) }
        // Some additional code that may or may not involve address
    }
    someFunc(&(UnsafeMutablePointer<[Int]>(bitPattern: address)!).pointee)

    print("sourceArray1 = \(sourceArray1)")
    print("sourceArray2 = \(sourceArray2)")
}

注意通过 Int 进行的指针转换。这是因为当 withUnsafeMutablePointer 返回时,它会在内部使指针 $0 失效,而如果你只是返回 $0,那么 withUnsafeMutablePointer 返回的指针也会失效。所以你必须欺骗 Swift 给你一些你可以在 withUnsafeMutablePointer 之外使用的东西。通过将其转换为 Int,您基本上将有效地址保存为数值。 Swift 不能让它失效。然后在 withUnsafeMutablePointer 之外,您必须将该 Int 地址转换回一个指针,UnsafeMutablePointer<T> 有一个初始化程序要做(毕竟,您可以想象一个带有内存映射 I/ O. 你需要读/写一个特定的地址来做 I/O。)任何时候你必须欺骗编译器让你做某事,这应该是一个很大的危险信号,也许你不应该这样做那。你可能仍然有很好的理由,但至少,它应该让你质疑他们,并考虑替代方案。

重要的是不要使用 address 来重建此范围之外的另一个指针。在这个特定的例子中,它在函数范围内仍然是一个有效地址,只是因为它是使用指针后引用的本地值的地址。当这些值超出范围时,使用任何指向它们的指针都会成为问题。如果它们是 class 的属性,让地址逃逸到 class 的范围之外,那么当实例被取消初始化时会出现问题。对于 struct 而言,问题发生得更快,因为它很可能最终会用于 struct 的副本而不是原始实例。

简而言之,当使用指针时,尽可能保持它们的本地化,并确保它们或任何可以在没有它们指向的原始 Swift“对象”的情况下重建它们的东西,不要逃逸到上下文之外您确定它们是有效的。这不是 C。您对分配的内存的生命周期没有太多控制权。通常在 Swift 中你不必担心它,但是当你使用指针时,实际上比在 C 中更难推理它们的有效性,因为你无法指定分配的内存何时变得无效.例如,在 Swift 中,保证本地分配的类实例将在作用域结束时保持“活动”状态。事实上,它通常在上次使用后立即解除初始化,即使在同一范围内可能有更多代码。如果你有一个指向这样一个对象的指针,即使你仍然是同一个作用域,你现在可能指向的是未初始化的内存。 Swift 甚至必须提供 withExtendedLifetime 来处理这种情况。这就是为什么 Swift 试图限制它们仅在 withUnsafePointer 函数系列中使用。这是唯一可以保证其有效性的上下文。在其他上下文中它们是有效的,但编译器无法证明它们是有效的。

,

显然,我最初在阅读问题时很模糊,所以有了更新的(希望是准确的)理解,我将通过提供我认为 OP 想要的具体(和可编译)代码来重新表达问题做,然后给出我的解决方案。

我重命名了一些东西,以便更具体地表达它们在代码中的作用。给定这样的代码:

var sourceArray1 = [1,4]
var sourceArray2 = [5,6,7,8]

func someFunc(_ someArray: inout [Int]) {
    someArray.indices.forEach { someArray[$0] = 0 }
}

let someCase = true // just for the sake of compiling a concrete case

//usage code
var arrayProxy: [Int]
if someCase {
    arrayProxy = sourceArray1
    // Some additional code that may or may not involve arrayProxy
} else {
    arrayProxy = sourceArray2
    // Some additional code that may or may not involve arrayProxy
}
someFunc(&arrayProxy)

print("sourceArray1 = \(sourceArray1)")
print("sourceArray2 = \(sourceArray2)")

实际输出为

sourceArray1 = [1,4]
sourceArray2 = [5,8]

但是想要的输出是

sourceArray1 = [0,0]
sourceArray2 = [5,8]

这样,if 语句选择的源数组就是要更改的数组,而 arrayProxy 尽管可能会在后面的代码中使用,但对于所需的目的并不重要效果在这里。

我不认为指针是正确的解决方案。我认为稍微修改设计会更好。需要的是更改源数组之一,那么为什么不这样做呢?我的解决方案是这样的:

var sourceArray1 = [1,8]

func someFunc(_ someArray: inout [Int]) {
    someArray.indices.forEach { someArray[$0] = 0 }
}

func caseTrue(_ someArray: inout [Int]) -> [Int]
{
    // The additional code from the if statement's `true` branch
    someFunc(&someArray)
    return someArray
}

func caseFalse(_ someArray: inout [Int]) -> [Int] {
    // The additional code from the if statement's `false` branch
    someFunc(&someArray)
    return someArray
}

let someCase = true // just for the sake of compiling a concrete case

//usage code - assuming arrayProxy is still needed for something in later code
let arrayProxy = someCase ? caseTrue(&sourceArray1) : caseFalse(&sourceArray2)

print("sourceArray1 = \(sourceArray1)")
print("sourceArray2 = \(sourceArray2)")

caseTruecaseFalse 可以是嵌套在包含 if 的方法中的局部函数,因此它们不会污染当前上下文之外的代码。

class AClass
{
    var sourceArray1 = [1,8]
    
    init() { }
    
    func someFunc(_ someArray: inout [Int]) {
        someArray.indices.forEach { someArray[$0] = 0 }
    }

    func aMethod()
    {
        func caseTrue(_ someArray: inout [Int]) -> [Int]
        {
            // The additional code from the if statement's `true` branch
            someFunc(&someArray)
            return someArray
        }

        func caseFalse(_ someArray: inout [Int]) -> [Int] {
            // The additional code from the if statement's `false` branch
            someFunc(&someArray)
            return someArray
        }
        
        let someCase = true // just for the sake of compiling a concrete case

        //usage code - assuming arrayProxy is still needed for something in later code
        let arrayProxy = someCase ? caseTrue(&sourceArray1) : caseFalse(&sourceArray2)

        print("sourceArray1 = \(sourceArray1)")
        print("sourceArray2 = \(sourceArray2)")
    }
}

如果 caseTruecaseFalse 的主体依赖于 aMethod 中的局部变量,这不是问题。局部函数就像闭包一样从周围的上下文中捕获变量(实际上,在 Swift 中它们基本上只是命名的闭包)。因此,在您需要调用它们之前声明这些函数,这意味着 aMethod 内部可能有一些位于它们之上的代码。

,

找到了一个解决方案,但看起来很奇怪:

var someProperty = [SomeClass]()
var someProperty2 = [SomeClass]()

func someFunc(someObject: inout [SomeClass]) {
    ...
    someObject = ... //
}

//usage code
let obj: UnsafeMutablePointer<[SomeClass]>
if someCase {
    obj = withUnsafeMutablePointer(to: &self.someProperty) { $0 }
    ...
} else {
    obj = withUnsafeMutablePointer(to: &self.someProperty2) { $0 }
    ...
}
someFunc(&obj.pointee)

因此仅更改了“使用代码”部分。我有一段时间没有找到合适的解决方案,因为例如以下代码会产生“悬空指针”警告:

obj = UnsafeMutablePointer<[SomeClass]>(&self.someProperty)

我的解决方案有时也会导致这个问题:

Thread 1: Simultaneous accesses to 0x7f85df805fc0,but modification requires exclusive access

本文链接:https://www.f2er.com/694100.html

大家都在问