声明一个指向 属性 的指针并将其作为输入参数传递给 Swift 中的函数?
Declare a pointer to a property and pass it as inout param into func in Swift?
我需要选择一些属性之一并通过引用传递它以在 func 中设置它。大概代码:
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
然后 obj
会更改但不会 someProperty
或 someProperty2
.
我读到我需要以某种方式将 obj
声明为 UnsafeMutablePointer
并且有类似的问题。但我不知道如何将它们应用于上面的代码以仅修复这 3 行(不更改其余代码):
let obj: [SomeClass]
obj = self.someProperty
obj = self.someProperty2
如何解决这个问题?
P.S。换句话说,我需要 let obj: inout [SomeClass]
之类的东西,但 Swift
不允许
显然我最初对这个问题的理解很模糊,所以有了更新的(希望是准确的)理解,我将通过给出我认为 OP 的具体(和可编译的)代码来重新表达这个问题想做,那就给出我的解决方案吧。
我对一些内容进行了重命名,以便更具体并表达它们在代码中的作用。给出这样的代码:
var sourceArray1 = [1, 2, 3, 4]
var sourceArray2 = [5, 6, 7, 8]
func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[[=10=]] = 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, 2, 3, 4]
sourceArray2 = [5, 6, 7, 8]
但期望的输出是
sourceArray1 = [0, 0, 0, 0]
sourceArray2 = [5, 6, 7, 8]
因此 if
语句选择的源数组就是要更改的数组,而 arrayProxy
尽管可能在以后的代码中使用,但对于以下目的并不重要想要的效果在这里。
我认为指针在这里不是正确的解决方案。我认为稍微修改一下设计会更好。需要的是改变其中一个源阵列,那么为什么不直接这样做呢?我的解决方案是:
var sourceArray1 = [1, 2, 3, 4]
var sourceArray2 = [5, 6, 7, 8]
func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[[=13=]] = 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)")
caseTrue
和 caseFalse
可以是嵌套在包含 if
的方法中的局部函数,因此它们不会污染当前上下文之外的代码。
class AClass
{
var sourceArray1 = [1, 2, 3, 4]
var sourceArray2 = [5, 6, 7, 8]
init() { }
func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[[=14=]] = 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)")
}
}
如果 caseTrue
和 caseFalse
的主体依赖于 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) { [=10=] }
...
} else {
obj = withUnsafeMutablePointer(to: &self.someProperty2) { [=10=] }
...
}
someFunc(&obj.pointee)
因此仅更改了“使用代码”部分。我有一段时间没有找到合适的解决方案,因为例如以下代码会产生“悬挂指针”警告:
obj = UnsafeMutablePointer<[SomeClass]>(&self.someProperty)
我的解决方案有时也会导致这个问题:
Thread 1: Simultaneous accesses to 0x7f85df805fc0, but modification requires exclusive access
在评论中讨论后,问题的目的是具体如何使用 UnsafeMutablePointer
来实现结果,而不是关于实现结果的最佳方法。
重要的是,从获取指针到使用它的整个代码都在 withUnsafeMutablePointer
范围内。因为它归结为在两个数组之间进行选择,然后通过别名将其中一个传递给 someFunc
,你不知道哪个指针必须保持活动状态,所以你必须让它们保持 都在直播。否则,当 Swift 使其无效时,您的程序可能会崩溃。
用指针达到预期效果的正确和安全的方法是这样的:
func pointerSolution()
{
var sourceArray1 = [1, 2, 3, 4]
var sourceArray2 = [5, 6]
func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[[=10=]] = 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, 2, 3, 4]
var sourceArray2 = [5, 6]
func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[[=12=]] = 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, 2, 3, 4]
var sourceArray2 = [5, 6]
func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[[=13=]] = 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: [=13=]) }
// Some additional code that may or may not involve address
} else {
address = withUnsafeMutablePointer(to: &sourceArray2) { Int(bitPattern: [=13=]) }
// Some additional code that may or may not involve address
}
someFunc(&(UnsafeMutablePointer<[Int]>(bitPattern: address)!).pointee)
print("sourceArray1 = \(sourceArray1)")
print("sourceArray2 = \(sourceArray2)")
}
注意通过Int
的指针转换。这是因为当 withUnsafeMutablePointer
returns 时,它在内部使指针 [=20=]
无效,如果你只是 return [=20=]
,指针就是 [=由 withUnsafeMutablePointer
编辑的 64=] 也无效。所以你必须欺骗 Swift
给你一些你可以在 withUnsafeMutablePointer
之外使用的东西。通过将其转换为 Int
,您基本上是将有效地址保存为数值。 Swift 无法使其失效。然后在 withUnsafeMutablePointer
之外,你必须将那个 Int
地址转换回一个指针,UnsafeMutablePointer<T>
有一个初始化程序要做(毕竟,你可以想象一个内存映射的嵌入式系统 I/O。你需要 read/write 到特定地址才能执行 I/O。)任何时候你必须欺骗编译器让你做某事,它应该是一个大危险信号,也许你不应该那样做。您可能仍然有充分的理由,但至少应该引起您的质疑,并考虑替代方案。
重要的是不要使用 address
重建此范围之外的另一个指针。在此特定示例中,它在函数范围内仍然是一个有效地址,仅因为它是在使用指针后引用的局部值的地址。当这些值超出范围时,使用任何指向它们的指针都会成为问题。如果它们是 class
的属性,那么当实例被取消初始化时,让地址转义到 class
的范围之外将是一个问题。对于 struct
,问题会更快发生,因为它很可能最终会用于 struct
的副本,而不是原始实例。
简而言之,在使用指针时,尽可能将它们保留在本地,并确保它们或任何可用于重建它们的东西没有它们指向的原始 Swift“对象”,不要不要逃到你确信它们有效的上下文之外。这不是 C。您对分配的内存的生命周期没有那么多的控制权。通常在 Swift 中你不必担心它,但是当你使用指针时,实际上比在 C 中更难推断它们的有效性,因为你无法指定何时分配的内存变得无效。例如,在 Swift 中, 而非 保证 class 的本地分配实例将在作用域结束时保持“活动”状态。事实上,它通常在最后一次使用后立即取消初始化,即使在同一范围内可能有更多代码。如果你有一个指向这样一个对象的指针,即使你仍然在同一个范围内,你现在也可能指向未初始化的内存。 Swift 甚至不得不提供 withExtendedLifetime
来处理这种情况。这就是为什么 Swift 试图将它们的使用限制在 withUnsafePointer
函数族内。这是唯一可以保证其有效性的上下文。在其他上下文中它们是有效的,但编译器无法证明它们是有效的。
我需要选择一些属性之一并通过引用传递它以在 func 中设置它。大概代码:
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
然后 obj
会更改但不会 someProperty
或 someProperty2
.
我读到我需要以某种方式将 obj
声明为 UnsafeMutablePointer
并且有类似的问题。但我不知道如何将它们应用于上面的代码以仅修复这 3 行(不更改其余代码):
let obj: [SomeClass]
obj = self.someProperty
obj = self.someProperty2
如何解决这个问题?
P.S。换句话说,我需要 let obj: inout [SomeClass]
之类的东西,但 Swift
显然我最初对这个问题的理解很模糊,所以有了更新的(希望是准确的)理解,我将通过给出我认为 OP 的具体(和可编译的)代码来重新表达这个问题想做,那就给出我的解决方案吧。
我对一些内容进行了重命名,以便更具体并表达它们在代码中的作用。给出这样的代码:
var sourceArray1 = [1, 2, 3, 4]
var sourceArray2 = [5, 6, 7, 8]
func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[[=10=]] = 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, 2, 3, 4]
sourceArray2 = [5, 6, 7, 8]
但期望的输出是
sourceArray1 = [0, 0, 0, 0]
sourceArray2 = [5, 6, 7, 8]
因此 if
语句选择的源数组就是要更改的数组,而 arrayProxy
尽管可能在以后的代码中使用,但对于以下目的并不重要想要的效果在这里。
我认为指针在这里不是正确的解决方案。我认为稍微修改一下设计会更好。需要的是改变其中一个源阵列,那么为什么不直接这样做呢?我的解决方案是:
var sourceArray1 = [1, 2, 3, 4]
var sourceArray2 = [5, 6, 7, 8]
func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[[=13=]] = 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)")
caseTrue
和 caseFalse
可以是嵌套在包含 if
的方法中的局部函数,因此它们不会污染当前上下文之外的代码。
class AClass
{
var sourceArray1 = [1, 2, 3, 4]
var sourceArray2 = [5, 6, 7, 8]
init() { }
func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[[=14=]] = 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)")
}
}
如果 caseTrue
和 caseFalse
的主体依赖于 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) { [=10=] }
...
} else {
obj = withUnsafeMutablePointer(to: &self.someProperty2) { [=10=] }
...
}
someFunc(&obj.pointee)
因此仅更改了“使用代码”部分。我有一段时间没有找到合适的解决方案,因为例如以下代码会产生“悬挂指针”警告:
obj = UnsafeMutablePointer<[SomeClass]>(&self.someProperty)
我的解决方案有时也会导致这个问题:
Thread 1: Simultaneous accesses to 0x7f85df805fc0, but modification requires exclusive access
在评论中讨论后,问题的目的是具体如何使用 UnsafeMutablePointer
来实现结果,而不是关于实现结果的最佳方法。
重要的是,从获取指针到使用它的整个代码都在 withUnsafeMutablePointer
范围内。因为它归结为在两个数组之间进行选择,然后通过别名将其中一个传递给 someFunc
,你不知道哪个指针必须保持活动状态,所以你必须让它们保持 都在直播。否则,当 Swift 使其无效时,您的程序可能会崩溃。
用指针达到预期效果的正确和安全的方法是这样的:
func pointerSolution()
{
var sourceArray1 = [1, 2, 3, 4]
var sourceArray2 = [5, 6]
func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[[=10=]] = 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, 2, 3, 4]
var sourceArray2 = [5, 6]
func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[[=12=]] = 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, 2, 3, 4]
var sourceArray2 = [5, 6]
func someFunc(_ someArray: inout [Int]) {
someArray.indices.forEach { someArray[[=13=]] = 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: [=13=]) }
// Some additional code that may or may not involve address
} else {
address = withUnsafeMutablePointer(to: &sourceArray2) { Int(bitPattern: [=13=]) }
// Some additional code that may or may not involve address
}
someFunc(&(UnsafeMutablePointer<[Int]>(bitPattern: address)!).pointee)
print("sourceArray1 = \(sourceArray1)")
print("sourceArray2 = \(sourceArray2)")
}
注意通过Int
的指针转换。这是因为当 withUnsafeMutablePointer
returns 时,它在内部使指针 [=20=]
无效,如果你只是 return [=20=]
,指针就是 [=由 withUnsafeMutablePointer
编辑的 64=] 也无效。所以你必须欺骗 Swift
给你一些你可以在 withUnsafeMutablePointer
之外使用的东西。通过将其转换为 Int
,您基本上是将有效地址保存为数值。 Swift 无法使其失效。然后在 withUnsafeMutablePointer
之外,你必须将那个 Int
地址转换回一个指针,UnsafeMutablePointer<T>
有一个初始化程序要做(毕竟,你可以想象一个内存映射的嵌入式系统 I/O。你需要 read/write 到特定地址才能执行 I/O。)任何时候你必须欺骗编译器让你做某事,它应该是一个大危险信号,也许你不应该那样做。您可能仍然有充分的理由,但至少应该引起您的质疑,并考虑替代方案。
重要的是不要使用 address
重建此范围之外的另一个指针。在此特定示例中,它在函数范围内仍然是一个有效地址,仅因为它是在使用指针后引用的局部值的地址。当这些值超出范围时,使用任何指向它们的指针都会成为问题。如果它们是 class
的属性,那么当实例被取消初始化时,让地址转义到 class
的范围之外将是一个问题。对于 struct
,问题会更快发生,因为它很可能最终会用于 struct
的副本,而不是原始实例。
简而言之,在使用指针时,尽可能将它们保留在本地,并确保它们或任何可用于重建它们的东西没有它们指向的原始 Swift“对象”,不要不要逃到你确信它们有效的上下文之外。这不是 C。您对分配的内存的生命周期没有那么多的控制权。通常在 Swift 中你不必担心它,但是当你使用指针时,实际上比在 C 中更难推断它们的有效性,因为你无法指定何时分配的内存变得无效。例如,在 Swift 中, 而非 保证 class 的本地分配实例将在作用域结束时保持“活动”状态。事实上,它通常在最后一次使用后立即取消初始化,即使在同一范围内可能有更多代码。如果你有一个指向这样一个对象的指针,即使你仍然在同一个范围内,你现在也可能指向未初始化的内存。 Swift 甚至不得不提供 withExtendedLifetime
来处理这种情况。这就是为什么 Swift 试图将它们的使用限制在 withUnsafePointer
函数族内。这是唯一可以保证其有效性的上下文。在其他上下文中它们是有效的,但编译器无法证明它们是有效的。