MonoBehaviour lifecycle and leaks explained

It was always bugging me. The theory is simple but it feels kinda lousy. I’m talking about how MonoBehaviour lifecycle is working.

11fDnTA

Please tell me, how often do you see messages like this? “Cleaning up leaked objects in scene since no game object, component or manager is referencing them. Material has been leaked 1 times.If you’re creating scripts with ExecuteInEditMode annotation and some of game objects, components, or materials you’re instancing on the fly I bet that this is well known phrase for you. What’s more interesting is that this is not an error nor warning. It’s just an info, little annoying info. Looks unprofessional isn’t it? Especially when you’re creating script in order to sell it to someone (like I do).

I must tell you something very, very important here. This message is misleading (maybe even a lie?). But before I go into it I want you to explain how MonoBehavior lifecycle is working.

MonoBehavior lifecycle

To test how MonoBehavior behaves in Unity I’ve created a simple script:

Here’s the results (function calls are listed in execution order):

When script has ExecuteInEditMode annotation

  • When new component (this script) is added: Awake(), OnEnable(), Reset(), Start()
  • When scene is saved (CTRL+S): nothing happens but here you may see those irritating info messages
  • When scene is unloaded: OnDisable(), OnDestroy()
  • When scene is loaded: Awake(), OnEnable(), Start()
  • When script is modified: OnDisable(), OnEnable()

When looking at these you can crearly see some contraries:

  • Awake – OnDestroy
  • OnEnable – OnDisable

This means that things created in Awake() should be destroyed in OnDestroy() as things done in OnEnable() should be undone in OnDisable(). One interesting thing is that Awake() is called only when script is being loaded (which is fine according to documentation) but when you’re developing your script only OnDisable() and OnEnable() will be called on the script reload. This may cause NullReferenceExceptions when you’ll decide to do some instancing in Awake() method and you’ll not reload your scene. Just keep that in mind.

When script is not annotated by ExecuteInEditMode

Just for the record.

  • In Edit mode nothing is executed (obvious, isn’t it?)
  • When entering play mode: Awake(), OnEnable(), Start()
  • When stopping: OnDisable(), Destroy()

The leak

I want to create a Material in my code. With knowledge of how MonoBehaviour is living I’ve decided to do it in OnEnable() method and destroy it in OnDisable() method. The reason is simple: I don’t want to reload the scene when I’ll change something that will affect the material code. This +should be enough:

Do you want to guess what will happen after I put this to game object and hit CTRL + S (Save Scene-)?

leaking

Bravo! Here it is!

If I think right this will happen every time when Unity will find dynamically created Material that is not attached to any Renderer component. Unity cannot verify if you’ll destroy the object so it fights back with this information above. So there must be a trick to tell Unity that I know what I am doing and I will clean my resources, right?

This magic formula is called HideFlags.DontSave. According to documentation “The object will not be saved to the scene. It will not be destroyed when a new scene is loaded“. So this is giving the responsibility for destroying the object back to us. Let’s try it:

And that’s it! No more messages about leaking objects! But be careful to always remove your objects by DestroyImmediate(). Not doing so may break Unity for someone who is using your code.

Categories: Blog, Unity 3D

Leave a Reply