有一个逻辑对象A,其中使用new GameObject创建了一个Root节点,然后从GameObjectPool中申请了一个资源(假设名为child),并将其挂在Root上。伪代码如下:
A::A()
{
root = new GameObject();
child = GameObjectPool.RequestGameObject("xxx");
child.Setparent(root);
}
当不再需要对象A时将其销毁,调用析构函数归还资源。伪代码如下:
A::~A()
{
GameObject.Destroy(root);
GameObjectPool.ReturnGameObject(child);
}
之后,由于逻辑需要,又创建了一个A1对象,从对象池中再次申请了“xxx”资源。此时A1对象表现异常。经查看A1中的child上挂接的组件全部被SetActive(false)。为什么呢?于是做了以下尝试:
A::~A()
{
GameObjectPool.ReturnGameObject(child);
GameObject.Destroy(root);
}
先还缓存池中的资源,再删除root。这样修改之后表现就正常了。
推测出错的原因应该是由于child挂在root上,直接删除root会导致child上的组件被deactive掉(GameObject.Destroy内部可能是这么干的)。但是此时child并没有被GC,紧接着我们调用了GameObjectPool.ReturnGameObject操作成功,又将未被GC的资源重新利用了起来。但此时child上面的组件已经被deactive掉了。所以表现异常了!
想要避免这种情况有几种方式:
1、要求必现先释放所有子节点,然后Destroy父节点,保证顺序。
2、通过封装屏蔽掉Destroy接口,不能直接调用GameObject.Destroy。统一调用GameObjectPool.Destroy。在GameObjectPool这一层保证Destroy不会被立即执行。
3、通过将资源封装在代理类中,父子关系的维护也放在代理类中。当需要销毁某一层的对象时,在代理内部会先断开所有父子关系。