文章目录
- 问题描述
- 分析
- 结果
- 总结
问题描述
后台日志大量报错,去主干看无法复现
c# exception:System.NullReferenceException: Object reference not set to an instance of an object. at FairyGUI.GCompone
nt.AddChildAt (FairyGUI.GObject child, System.Int32 index) [0x00000] in <00000000000000000000000000000000>:0 at Fairy
GUI.GList.AddChildAt (FairyGUI.GObject child, System.Int32 index) [0x00000] in <00000000000000000000000000000000>:0 a
t FairyGUI.GComponent.AddChild (FairyGUI.GObject child) [0x00000] in <00000000000000000000000000000000>:0 at FairyGUI
.GList.set_numItems…
分析
看逻辑是没有问题的,但是在调用numItems时c#层报错。看FairyGUI的代码,
public int numItems
{get{if (_virtual)return _numItems;elsereturn _children.Count;}set{if (_virtual){if (itemRenderer == null)throw new Exception("FairyGUI: Set itemRenderer first!");_numItems = value;if (_loop)_realNumItems = _numItems * 6;//设置6倍数量,用于循环滚动else_realNumItems = _numItems;//_virtualItems的设计是只增不减的int oldCount = _virtualItems.Count;if (_realNumItems > oldCount){for (int i = oldCount; i < _realNumItems; i++){ItemInfo ii = new ItemInfo();ii.size = _itemSize;_virtualItems.Add(ii);}}else{for (int i = _realNumItems; i < oldCount; i++)_virtualItems[i].selected = false;}if (_virtualListChanged != 0)Timers.inst.Remove(this.RefreshVirtualList);//立即刷新this.RefreshVirtualList(null);}else{int cnt = _children.Count;if (value > cnt){for (int i = cnt; i < value; i++){if (itemProvider == null)AddItemFromPool();elseAddItemFromPool(itemProvider(i));}}else{RemoveChildrenToPool(value, cnt);}if (itemRenderer != null){for (int i = 0; i < value; i++)itemRenderer(i, GetChildAt(i));}}}
}
AddChild 发生在 AddItemFromPool 中
/// <summary>
/// Add a item to list, same as GetFromPool+AddChild
/// </summary>
/// <returns>Item object</returns>
public GObject AddItemFromPool()
{GObject obj = GetFromPool(null);return AddChild(obj);
}/// <summary>
/// Add a item to list, same as GetFromPool+AddChild
/// </summary>
/// <param name="url">Item resource url</param>
/// <returns>Item object</returns>
public GObject AddItemFromPool(string url)
{GObject obj = GetFromPool(url);return AddChild(obj);
}
AddChild 的参数为空引用, 看GetFromPool如何拿到
public GObject GetFromPool(string url)
{if (string.IsNullOrEmpty(url))url = _defaultItem;GObject ret = _pool.GetObject(url);if (ret != null)ret.visible = true;return ret;
}
最终定位到GObjectPool.GetObject,对象是从对象池池子中取出的,没取到则创建一个
public GObject GetObject(string url){url = UIPackage.NormalizeURL(url);if (url == null)return null;Queue<GObject> arr;if (_pool.TryGetValue(url, out arr)&& arr.Count > 0)return arr.Dequeue();GObject obj = UIPackage.CreateObjectFromURL(url);if (obj != null){if (initCallback != null)initCallback(obj);}return obj;}
从上面的代码可以得出报错的原因
- 可能池子里拿出来的对象在lua层已经被销毁了
- 可能创建对象没有成功,资源存在问题
在lua代码报错的地方加入以下定位代码
local list = self._listviewlocal obj, nullNum, lossNumfor i = list.numChildren + 1, infoLen do -- 先处理已经销毁了的obj, nullNum, lossNum = self:getItmeFromPool(list)if obj thenlist:AddChild(obj)elseif SDKCtrl thenSDKCtrl:reportError(" warning 上报定位 get null tiems:" .. nullNum .. " get Disposed times: " .. lossNum)endendendlist.numItems = infoLenlist:ResizeToFit(infoLen)
function mod:getItmeFromPool(list)local objlocal lossNum, nullNum = 0, 0while lossNum < 100 and nullNum < 100 doobj = list:GetFromPool()if not obj thennullNum = nullNum + 1elseif obj.isDisposed thenlossNum = lossNum + 1elsebreakendendreturn obj, nullNum, lossNum
end
结果
定位的代码更新出去后,日志中出现了 warning 上报定位 get null tiems:100 get Disposed times: 0。
item对象没有创建成功,是分支的列表item资源存在问题,分支找到对应的包重新发布下,问题解决了。应该是主干同步分支的时候没有发布该包。
总结
GList添加item时候,会从对象池中取一个已存在对象复用或者创建新的对象,如果没有找到对应的资源或者对象池中的对象已经销毁过,会导致创建失败,报错 AddChildAt NullReferenceException。