OnBeforeSerialize is getting called rapidly?

Not sure if this is normal, but I noticed that OnBeforeSerialize is getting called very rapidly in-editor and at runtime - almost every frame.

using UnityEngine;   
  
public class Test : MonoBehaviour, ISerializationCallbackReceiver
{
    public void OnBeforeSerialize()
    {
        Debug.Log("before");
    }
  
    public void OnAfterDeserialize()
    {
        Debug.Log("after");
    }
}

I read in the docs that it’s called before Unity ‘reads’ my objects’ values - I don’t think ‘read’ is quite a synonym to ‘serialize’ - they’re using ‘read’/‘write’ to ‘serialization’/‘deserialization’ - not sure about that.

Is this how it’s supposed to work? (called very rapidly) - if so, I think they should have named it something else cause OnBeforeSerialize is miss-leading, unless I’m miss-understanding something here.

Cheers!

EDIT:

So I thought maybe cause I’m using some Unity API (Debug.Log) which the doc states that it wouldn’t be preferred - but not the case, tried this:

public int counter;

public void OnBeforeSerialize()
{
    counter++;
}

And the value still changes very rapidly!

I would agree that “happens more often than you might think” doesn’t really equate to “every frame”.

Here’s what I found for OnBeforeSerialize:

  1. Called once before a recompile is handled by the Unity Editor
  2. Called multiple times when the scene is saved (ie two or three times)
  3. Called every frame if the inspector for the object is open in the editor
  4. Not called in an actual build (in my simple test)

Here’s what I found for OnAfterDeserialize:

  1. Called once after a recompile
  2. Called once after the scene is loaded
  3. Called once in an actual build (in my simple test)

So, if you have the editor open and an inspector open. OnBeforeSerialize is called every frame by the OnInspectorUpdate function. So far, there doesn’t seem to be a way for me to stop or interrupt this.

The documentation warns you that using this interface and the rapid calls right here:

Another caveat is that serialization happens more often than you might think. When a MonoBehaviour gets cloned through Instantiate() at runtime, it gets deserialized and serialized. If the object had a field that pointed to its own gameObject, when that gameObject gets cloned, you’d want the field to point to the new gameObject, not the old one. In order to update these “internal references”, Unity uses the serialization system. Unity reads all your fields, finds references to “things that were cloned”, and then writes the new values in your fields. The callbacks will also be invoked for this updating phase, allowing you to make the updating of internal references to also work in your custom classes that Unity cannot serialize.

This is an old thread, but here’s a workaround.

I have a ScriptableObject that have some properties exposed in the inspector, but I use the serialization callbacks to serialize a non-serializable data (as an example, I’m serializing a dictionary).

In order to avoid serialization if it’s not needed, I use the Unity’s dirty flag. This flag is enabled when an exposed property is changed in the inspector. So if this flag is set to true (and so a property has been modified), I let the serialization happen, then I remove the dirty flag.

public void OnBeforeSerialize()
{
    #if UNITY_EDITOR
    if(UnityEditor.EditorUtility.IsDirty(this);
    {
        SerializeDictionary();
        UnityEditor.EditorUtility.ClearDirty(this);
    }
    #else
    SerializeDictionary();
}

private void SerializeDictionary() { /* ... */ }

Hi everyone! :tada: Happy 10-year anniversary of this issue!

While working on a custom serialization solution, I encountered this problem and came across this thread. I’d like to share a working solution that can be applied more broadly than just to ScriptableObjects.

The idea is to examine the stack trace and skip the execution of OnBeforeSerialize based on a custom condition. As discussed in this thread, the method is often called every frame due to UnityEditor.ActiveEditorTracker:VerifyModifiedMonoBehaviours().

Here’s how you can handle this situation:

void ISerializationCallbackReceiver.OnBeforeSerialize()
{
#if UNITY_EDITOR
    System.Diagnostics.StackTrace trace = new();
    System.Diagnostics.StackFrame previousFrame = trace.GetFrame(1);

    if (previousFrame?.GetMethod()?.Name == "Internal_VerifyModifiedMonoBehaviours")
        return;
#endif

    _serializedValue = SerializationContext.Serialize(Value);
}

With this approach, you can prevent unnecessary serialization during specific Unity editor processes, improving performance and avoiding redundant work. I hope this helps others tackling similar issues!


Does this look good to you, or would you like to adjust the tone or content further?