Golang中接收器为nil的问题如何解决
Golang中接收器为nil的问题如何解决 我一定是误解了什么,当我这样写时:
_, err = m.ToolbarRefresh.Connect("clicked", m.FundList.updateFundsValue)
其中 updateFundsValue() 是声明在 fundList 类型上的函数:
func (f *fundList) updateFundsValue() {
go func() {
...
}()
}
我在运行时得到一个错误:
panic: runtime error: invalid memory address or nil pointer dereference
因为在 updateFundsValue() 内部,接收者 f 是 nil。但是当我这样写时,它却能正常工作:
_, err = m.ToolbarRefresh.Connect("clicked", func() {
m.FundList.updateFundsValue()
})
在这种情况下,m.FundList 有一个值,并且接收者 f 也有一个值。这是为什么呢?第一种方式在其他情况下是可行的,例如:
_, err = m.ToolbarQuit.Connect("clicked", m.shutDown)
附注:这段代码使用的是 GoTK3(https://github.com/gotk3/gotk3),但我认为这个问题与 GoTK3 无关,因为第二种情况工作正常。当有一个类型 m 指向另一个类型 FundList 的指针时,难道不能使用单行代码吗?这似乎很随机……
更多关于Golang中接收器为nil的问题如何解决的实战教程也可以访问 https://www.itying.com/category-94-b0.html
从语法上讲,没有理由它不应该工作。你在调用 Connect 后检查错误值了吗?在你点击时,m 和 m.FundList 是非 nil 的吗?
更多关于Golang中接收器为nil的问题如何解决的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html
skillian:
从语法上讲,没有理由它不应该工作。
这很好,我之前从未遇到过这个问题,所以我没想到这里会有任何问题……但确实出现了问题……
skillian:
你在调用
Connect之后检查错误值了吗?
是的,并且没有错误……
skillian:
在你点击时,
m和m.FundList是非空值吗?
它们应该是的。m 是一个指向主窗体的指针,当按钮被点击时该窗体仍然处于打开状态,而 m.FundList 是一个指针,只有在我关闭主窗体时(在 shutdown() 函数中)才会被清空。
无论如何,我认为你的回答——即它应该能工作——目前对我来说已经足够了。要么是 GoTK3 中发生了一些奇怪的事情,或者更可能的是,我在代码的某个地方犯了一些愚蠢的错误,这些错误我将来可能会(也可能不会)发现。重要的是我现在有一个变通方法(那三行代码)。
我问这个问题的原因是,我担心我遗漏了关于接收器和指针的某个要点,而这个要点我可能本应知道。
这是一个典型的Go语言方法值(method value)与nil接收器的问题。问题的关键在于方法值的求值时机。
在第一种写法中:
_, err = m.ToolbarRefresh.Connect("clicked", m.FundList.updateFundsValue)
m.FundList.updateFundsValue 是一个方法值(method value)。当创建方法值时,它会立即对接收器 m.FundList 进行求值。如果此时 m.FundList 是 nil,那么创建的方法值就绑定了一个 nil 接收器。当这个函数被调用时,接收器 f 就是 nil,导致 panic。
在第二种写法中:
_, err = m.ToolbarRefresh.Connect("clicked", func() {
m.FundList.updateFundsValue()
})
这里创建了一个匿名函数,在匿名函数内部调用 m.FundList.updateFundsValue()。这个调用是在匿名函数执行时才发生的,而不是在创建时。如果到执行时 m.FundList 已经被正确初始化,那么接收器就不会是 nil。
要解决这个问题,你需要确保在创建方法值时接收器不是 nil。有几种方法:
- 延迟初始化:确保在调用
Connect之前初始化m.FundList
// 先初始化 FundList
m.FundList = &fundList{}
// 然后再连接信号
_, err = m.ToolbarRefresh.Connect("clicked", m.FundList.updateFundsValue)
- 使用闭包包装:就像你发现的第二种写法那样
_, err = m.ToolbarRefresh.Connect("clicked", func() {
if m.FundList != nil {
m.FundList.updateFundsValue()
}
})
- 检查方法值创建时的接收器:
if m.FundList != nil {
_, err = m.ToolbarRefresh.Connect("clicked", m.FundList.updateFundsValue)
}
你提到的 m.shutDown 能正常工作的原因可能是:
m.shutDown的接收器是m本身,而m在调用时已经初始化- 或者
shutDown是一个值接收器方法,而不是指针接收器方法
示例代码展示问题本质:
type Handler struct {
data string
}
func (h *Handler) Process() {
if h == nil {
println("接收器是nil")
return
}
println("处理数据:", h.data)
}
func main() {
var h *Handler
// 方法值立即求值接收器
f1 := h.Process // 此时h是nil,但不会panic
// 调用时接收器是nil
f1() // 输出: 接收器是nil
// 闭包方式
f2 := func() {
h.Process() // 调用时才求值
}
h = &Handler{data: "test"}
f2() // 输出: 处理数据: test
}
在你的 updateFundsValue 方法中,由于没有检查接收器是否为 nil 就直接使用了接收器的字段或方法,所以导致了 panic。建议在方法开始时添加 nil 检查:
func (f *fundList) updateFundsValue() {
if f == nil {
return
}
go func() {
// 使用 f 的代码
}()
}

