由UnityEngine.Object该怎么判空引发的一些思考

1.测试数据分析

  先给出一组测试数据,分别用三种比较方式进行100万次UnityEngine.Object的判空。

比较方式 耗时(毫秒)
obj == null 43
obj is null 4
System.Object.ReferenceEquals(obj, null) 4

  一开始的时候用GameObject和继承MonoBehaviour组件拿到的测试结果不一样,GameObject用ReferenceEquals或者 is null比较速度只能提升一倍,后来发现是忘记缓存gameObject了(在MonoBehaviour脚本里直接写的gameobject是一个属性,虽然一般不会造成性能问题,但在测试时不加注意可能会得到错误的结论)。
  拿到这个数据后就可以开始猜测了,is null就是一个语法糖,通过上述数据可以猜测is null 会被翻译成ReferenceEquals。那么ReferenceEquals为什么会快这么多呢。

2.ReferenceEquals

  既然ReferenceEquals要快这么多,那就去看一下实现,不看不要紧,一看吓一跳,这不就是 == 么。一开始还怀疑是不是上面加的两个特性捣的鬼,查了一下排除了。

[ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success)]
[System.Runtime.Versioning.NonVersionable]
public static bool ReferenceEquals (Object objA, Object objB) {
    return objA == objB;
}

  那么会不会是函数参数是system.object造成的呢,于是增加两种比较方式得到以下结果

比较方式 耗时(毫秒)
obj == null 43
obj is null 4
(object)obj == (object)null 4
SelfEquals(obj, null) 9

SelfEquals实现如下

bool SelfEquals(object obj1, object obj2)
{
    return obj1 == obj2;
}

  到这里事情就有点意思了,到这时可以暂时理解为把UnityEngine.Object转成System.Object比较会快一些。自己写SelfEquals慢一点估计是因为多了一层函数调用。

  最后用ILSpy反编译一下unity生成的dll,发现直接使用==时,编译器翻译的最终c#代码是这样的

if ((Object)(object)obj == (Object)null){}

  而其它几种都是这样的

if (obj == null){}

  如果比较的不是UnityEngine.Object,而是继承自System.Object的类的话则最终翻译到的c#代码都是第二种情况。

3.总结

  unity有三大内存域,跨域的通信过渡会造成性能上的损失,由于UnityEngine.Object的数据是保存在本地域上,直接使用 == 时可能会引发本地域和托管域之间的桥梁通信过度,从而造成速度变慢。而把他们当作System.Object做比较时,应该是只在托管域中做了一次引用比较。

  • 本地域
  • 托管域
  • 外部dll

你可能感兴趣的:(Unity,unity)