Search Unity

  1. Welcome to the Unity Forums! Please take the time to read our Code of Conduct to familiarize yourself with the forum rules and how to post constructively.
  2. We have updated the language to the Editor Terms based on feedback from our employees and community. Learn more.
    Dismiss Notice
  3. Join us on November 16th, 2023, between 1 pm and 9 pm CET for Ask the Experts Online on Discord and on Unity Discussions.
    Dismiss Notice

Unity 5.1 Has broken the use of singleton patterns?

Discussion in 'Scripting' started by pixelfat, Jun 11, 2015.

  1. pixelfat

    pixelfat

    Joined:
    Apr 18, 2015
    Posts:
    12
    Hey guys, just updated to 5.1 and I'm getting errors such as
    and
    The latter of the two being a real deal-breaker for singletons. I've been using this particular class for over 5 years now and I'd really not like to have to remove it!

    Does anyone have a work-around or tips? Maybe I'm just missing something simple?

    For good measure, here's my (now busted) singleton class. It's a common one with a few tweaks I've added over the years...
    Code (CSharp):
    1. using UnityEngine;
    2.  
    3. namespace com.pixelfat.unity.utility
    4. {
    5.  
    6.     /// <summary>
    7.     /// Be aware this will not prevent a non singleton constructor
    8.     ///   such as `T myT = new T();`
    9.     /// To prevent that, add `protected T () {}` to your singleton class.
    10.     ///
    11.     /// As a note, this is made as MonoBehaviour because we need Coroutines.
    12.     /// </summary>
    13.     public class Singleton<T> : MonoBehaviour where T : MonoBehaviour
    14.     {
    15.         private static T _instance;
    16.      
    17.         private static object _lock = new object();
    18.  
    19.         protected Singleton () {}
    20.  
    21.         public static T Instance
    22.         {
    23.             get
    24.             {
    25.                 if (applicationIsQuitting)
    26.                 {
    27.  
    28.                     if(_instance == null)
    29.                         Debug.LogWarning("[Singleton] Instance '"+ typeof(T) +
    30.                                          "' already destroyed on application quit." +
    31.                                          " Won't create again - returning null.");
    32.  
    33.                     return _instance;
    34.  
    35.                 }
    36.              
    37.                 lock(_lock)
    38.                 {
    39.                     if (_instance == null)
    40.                     {
    41.                         _instance = (T) FindObjectOfType(typeof(T));
    42.                      
    43.                         if ( FindObjectsOfType(typeof(T)).Length > 1 )
    44.                         {
    45.                             Debug.LogError("[Singleton] Something went really wrong " +
    46.                                            " - there should never be more than 1 singleton!" +
    47.                                            " Reopenning the scene might fix it.");
    48.                             return _instance;
    49.                         }
    50.                      
    51.                         if (_instance == null)
    52.                         {
    53.                             GameObject singleton = new GameObject();
    54.                             _instance = singleton.AddComponent<T>();
    55.                             singleton.name = "(singleton) "+ typeof(T).ToString();
    56.  
    57.                             Singleton<T> _s = _instance as Singleton<T>;
    58.                             _s.Init();
    59.  
    60.                             DontDestroyOnLoad(singleton);
    61.                          
    62.                             Debug.Log("[Singleton] An instance of " + typeof(T) +
    63.                                       " is needed in the scene, so '" + singleton +
    64.                                       "' was created with DontDestroyOnLoad.");
    65.                         } else {
    66.                             Debug.Log("[Singleton] Using instance already created: " +
    67.                                       _instance.gameObject.name);
    68.                         }
    69.                     }
    70.                  
    71.                     return _instance;
    72.                 }
    73.             }
    74.         }
    75.      
    76.         private static bool applicationIsQuitting = false;
    77.         /// <summary>
    78.         /// When Unity quits, it destroys objects in a random order.
    79.         /// In principle, a Singleton is only destroyed when application quits.
    80.         /// If any script calls Instance after it have been destroyed,
    81.         ///   it will create a buggy ghost object that will stay on the Editor scene
    82.         ///   even after stopping playing the Application. Really bad!
    83.         /// So, this was made to be sure we're not creating that buggy ghost object.
    84.         /// </summary>
    85.         public void OnDestroy () {
    86.             applicationIsQuitting = true;
    87.         }
    88.  
    89.         protected virtual void Init() {;}
    90.  
    91.     }
    92.  
    93. }
     
    Last edited: Jun 11, 2015
  2. Sbizz

    Sbizz

    Joined:
    Oct 2, 2014
    Posts:
    250
    Why are you locking your code ?

    You should NEVER use Unity's components in another thread.

    BTW, you should use "FindObjectsOfType", store the result, check if there are not more than one result and if not, take the first index. Doing this, you will not use "FindObject(s)OfType" twice.
     
  3. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    You may have an Awake in your singleton where you just call Instance in order to initialize it. When you make sure that the other threads are not accessing it too early in the first frame, it should work as far as I can see.
     
  4. pixelfat

    pixelfat

    Joined:
    Apr 18, 2015
    Posts:
    12
    I've removed the lock and also all references to FindObjectsOfType for test purposes, now it's just simply:
    Code (CSharp):
    1.                  
    2.   if (_instance == null) {
    3.  
    4.                         _instance = new GameObject("instance", typeof(T)).GetComponent<T>();
    5.  
    6.                         _instance.name = "(singleton) "+ typeof(T).ToString();
    7.  
    8.                         Singleton<T> _s = _instance as Singleton<T>;
    9.                         _s.Init();
    10.  
    11.  
    Unfortunately, the second line is still throwing the error. (Assigning the new instance to _instance).
     
  5. pixelfat

    pixelfat

    Joined:
    Apr 18, 2015
    Posts:
    12
    The error is being thrown before any of my classes are used at C:/buildslave/unity/build/artifacts/generated/common/runtime/UnityEngineGameObjectBindings.gen.cs:424

    It's definitely related to a change in Unity 5.1, no idea which one though...
     
  6. Dantus

    Dantus

    Joined:
    Oct 21, 2009
    Posts:
    5,667
    What happens in the other thread that is calling your singleton? Can't you delay the creation of the thread?
     
  7. pixelfat

    pixelfat

    Joined:
    Apr 18, 2015
    Posts:
    12
    Update: Turns out it was because of an assignment that was just damned hard to find:

    Code (CSharp):
    1. private ChallengeEditorCursor.CURSOR_STATE mCursorControlState = ChallengeEditorCursor.Instance.state;
    Weird that it only started happening since the 5.1 update, but happy to have found it and learned a bit! Thanks guys, beers are owed!!
     
    Last edited: Jun 11, 2015
    NomadKing likes this.