如何在 Go 中保持强引用?
How to keep a strong reference in Go?
有什么方法可以在 Go 中保留强引用?
给定以下复杂的代码:
package main
import (
"fmt"
)
func main() {
slice := make([]int, 5)
slice[3] = 25 // whatever index between 0 and 4 included I don't care
slicesArray := make([]*[]int, 2)
slicesArray[0] = &slice
fmt.Println((*(slicesArray[0]))[3])
slice = nil
fmt.Println((*(slicesArray[0]))[3])
}
这个程序当然会崩溃,因为一旦将 slice 设置为 nil
,垃圾收集器就会将内存区域标记为脏。
但是有没有办法告诉 Go 我的切片指针切片应该保持对这些切片的强引用?
此外,保留对切片的引用而不是将 slicesArray 声明为 [][]int
是否会增加内存使用量?是否有文档清楚地说明这是如何工作的?
TL;DR:(摘要)
您只需复制切片值即可。切片类型的值是 底层 数组的描述符:
slice2 := slice
现在 slice2
将引用相同的共享底层数组,因此在 slice
归零后可以访问它。
长:
slice := make([]int, 5)
这将创建一个名为 slice
类型为 []int
的局部变量(并将使用引用由 [=20 在后台创建的大小为 5
的数组的描述符进行初始化=]).它是一个切片,它是在后台自动创建的底层数组的连续部分的描述符。
slicesArray[0] = &slice
这会将局部变量(名为slice
)的地址存储到slicesArray
的第0 个元素中。请注意 slicesArray
只是指针的一部分(这些指针可能指向 []int
类型的值,但现在这无关紧要)。
所以仍然没有创建原始 slice
的副本。
因此,当您将 []int
类型的唯一值(即名为 slice
的局部变量)归零时,您将唯一的切片值归零(并且您将丢失对它的后备阵列)。
您想在 slice
归零后保留切片值吗?只需复制它(切片值)。复制 slice 类型的值只会复制描述符,不会复制支持数组;并且副本将引用相同的后备数组(它是共享的):
slice2 := slice // makes a copy of the slice value
slice = nil
此后 *(slicesArray[0])
仍将指向切片值 nil
,但我们有原始切片的副本(和共享后备数组)。
这样做:
slicesArray[0] = &slice2
fmt.Println((*(slicesArray[0]))[3])
将再次打印 25
。 Go Playground
应该保留切片值还是切片指针?
由于切片只是相对较小的描述符,您应该保留并使用切片值。一个切片值已经包含一个 "reference" 到后备数组。通过使用指向切片的指针添加另一个间接只会使事情复杂化并稍微减慢速度。切片已经被设计成小巧、高效和灵活的(与 Go 中的 "real" 数组相比)。
当然,在某些情况下,指向切片值的指针也可能有用,例如,如果您想要创建一个类似于内置 append()
函数的函数而不返回新切片。或者,当您创建一个其底层类型为切片类型的自定义类型时,您为该类型定义了修改切片值的方法(在这种情况下,指针接收器是必需的)。
进一步阅读:(文档详细解释了一切)
有什么方法可以在 Go 中保留强引用?
给定以下复杂的代码:
package main
import (
"fmt"
)
func main() {
slice := make([]int, 5)
slice[3] = 25 // whatever index between 0 and 4 included I don't care
slicesArray := make([]*[]int, 2)
slicesArray[0] = &slice
fmt.Println((*(slicesArray[0]))[3])
slice = nil
fmt.Println((*(slicesArray[0]))[3])
}
这个程序当然会崩溃,因为一旦将 slice 设置为 nil
,垃圾收集器就会将内存区域标记为脏。
但是有没有办法告诉 Go 我的切片指针切片应该保持对这些切片的强引用?
此外,保留对切片的引用而不是将 slicesArray 声明为 [][]int
是否会增加内存使用量?是否有文档清楚地说明这是如何工作的?
TL;DR:(摘要)
您只需复制切片值即可。切片类型的值是 底层 数组的描述符:
slice2 := slice
现在 slice2
将引用相同的共享底层数组,因此在 slice
归零后可以访问它。
长:
slice := make([]int, 5)
这将创建一个名为 slice
类型为 []int
的局部变量(并将使用引用由 [=20 在后台创建的大小为 5
的数组的描述符进行初始化=]).它是一个切片,它是在后台自动创建的底层数组的连续部分的描述符。
slicesArray[0] = &slice
这会将局部变量(名为slice
)的地址存储到slicesArray
的第0 个元素中。请注意 slicesArray
只是指针的一部分(这些指针可能指向 []int
类型的值,但现在这无关紧要)。
所以仍然没有创建原始 slice
的副本。
因此,当您将 []int
类型的唯一值(即名为 slice
的局部变量)归零时,您将唯一的切片值归零(并且您将丢失对它的后备阵列)。
您想在 slice
归零后保留切片值吗?只需复制它(切片值)。复制 slice 类型的值只会复制描述符,不会复制支持数组;并且副本将引用相同的后备数组(它是共享的):
slice2 := slice // makes a copy of the slice value
slice = nil
此后 *(slicesArray[0])
仍将指向切片值 nil
,但我们有原始切片的副本(和共享后备数组)。
这样做:
slicesArray[0] = &slice2
fmt.Println((*(slicesArray[0]))[3])
将再次打印 25
。 Go Playground
应该保留切片值还是切片指针?
由于切片只是相对较小的描述符,您应该保留并使用切片值。一个切片值已经包含一个 "reference" 到后备数组。通过使用指向切片的指针添加另一个间接只会使事情复杂化并稍微减慢速度。切片已经被设计成小巧、高效和灵活的(与 Go 中的 "real" 数组相比)。
当然,在某些情况下,指向切片值的指针也可能有用,例如,如果您想要创建一个类似于内置 append()
函数的函数而不返回新切片。或者,当您创建一个其底层类型为切片类型的自定义类型时,您为该类型定义了修改切片值的方法(在这种情况下,指针接收器是必需的)。
进一步阅读:(文档详细解释了一切)