Unity中调用Awake,Start,Update等方法的原理机制

首先声明这里的理解并不是官方释义,只是舶主根据晚上的各位大佬的关点理解总结的,如果有什么不对的地方希望大家指正:

作为Unity中的生命周期准确的闹明白它的实现原理还是很重要的我认为。生命周期方法的实现机制实际上是一种类反射机制。Uniy引擎整体是架设在mono IDE基础上的。mono IDE是支持通过string来查找方法的,且和真正的反射一样是能够查找调用私有方法的,当找到这些方法后会存下指针,待使用。在引擎内部存在一张表,这张表的形成是通过在场景中查找全部的MonoBehaviour

类型脚本然后便利里面的方法,将需要的调用的方法是全部记录下来,然后进行调用。防止那些不需要调用的MonoBehaviour中的方法占用空间,节省资源开耗。Mono的API可能效率更高,但对于单次调用,性能仍然逊于虚方法调用。反射实际上是开销非常大的调用方式,比之虚方法来说要高得多,因此Unity选择使用反射并非是出于性能方面的考虑。实际上,每帧调用的这些事件方法从数量级上来说是很卑微的,反射造成的性能影响亦可忽略不计。

 Unity使用这种事件机制的根本原因是出于对灵活性的考虑。Unity采用组件式设计,触发一个事件,需要通知到相应gameobject的所有组件。如果使用多态来实现,则必须假设所有组件都派生自包含此事件的基类,或者筛选出派生自此基类的组件逐一通知。这样一来是麻烦,二来则容易带来复杂的继承关系,与组件式这种倡导用聚合代替继承的设计从理念上就是相悖的。
另一方面的原因则是为了跟JS保持一致性。这种事件机制对于JS这种动态类型语言来说是浑然天成的。
这种设计最大的缺陷在于事件名通过字符串耦合,如此一来,完全绕开了编译期静态检查,无法为事件调用的正确性提供保障;在复杂的系统里,也可能因为事件重名而导致bug。
 

Unity的确是通过反射来调用脚本的方法的,并且这一过程会在运行时不停对所有MonoBehaviour遍历进行。

Unity之所以统一地使用这一套固定的函数命名方案,便于明确地划出了每个函数需要做些什么。这样做的目的我猜测是有利用保留脚本的灵活性。这种做法被惯称为“事件机制”,一旦某个脚本被执行完成之后,它的控制权会重新回到调度管理处,可以轻松地再去执行下一个,并且也能在运行时通过反射方式让其它脚本使用Component.SendMessage进行调用。

如果采用了抽象方式让子类去实现这样的方法,那么对于Unity本身的对象管理是没有任何好处的,并且对于拥有多个脚本组件的对象来说,维护成本不但增加了,还可能让脚本之间的管理变得混乱。

使用反射也许会丢失一些性能,但却能让每个不同的MonoBehaviour之间看起来都是独立的,只需要在它提供的几个内置方法中关注自己的逻辑就可以了。
posted @ 2017-09-12 11:03  戴草帽的小子  阅读(9269)  评论(0编辑  收藏  举报