本文通过一个最常见的,lua和C#/C++循环引用导致的内存泄露案例来分析原因和解决方案。
按钮绑定匿名函数(闭包)导致的循环引用:
self.Button.OnClicked:Add(function()
self:doSomething(true)
end)
循环引用链:
UIBP(self)------->Button------>luaFunction------>UIBP(self)
lua引用到C++对象Button,Button又引用到了lua的闭包,闭包内使用了一个upvalue——self,而这个self指的是一个UIBP,UIBP又引用到了Button
简单来说就是lua函数引用到了C++对象,这个C++对象又引用回了同一个lua函数。导致GC检测引用的时候,这个UI始终无法被回收
解除循环引用链中的某一个引用即可
self.Button.OnClicked:RemoveAll()
解除委托的绑定以后,就不存在Button对luaFunction的引用了,也就打破了循环引用链:
UIBP(self)------->Button—×—>luaFunction------>UIBP(self)
function UIBPName:f1()
self:doSomething(true)
end
self.Button.OnClicked:Add(self.f1, self)
Add
方法对第一个参数是强引用,第二个参数则是弱引用。相当于打破了循环引用链:
UIBP(self)------->Button------>luaFunction—×—>UIBP(self)
对第一个参数强引用的原因是,保证匿名函数可以作为第一个参数传入。
local f1 = func()
UIBP:doSomething(true)
end
self.Button.OnClicked:Add(f1)
这种方式可以保证即便忘记解绑,也不会出现内存泄露
Tips:
这种 local f1 最好不要写在函数里面,防止上值存储