Revolter
Revolter

Reputation: 45

Access static class directly or cache it?

I know that it's better to cache a component if you use it often during runtime. How about if I cache a component as a static variable and access it during runtime? Let me give you an example:

public class AudioManager : MonoBehaviour
{
    public static AudioManager audioManager;

    void Awake()
    {
        audioManager = this;
    }
}

Then if I access it from another class, should I use AudioManager.audioManager or cache it beforehand (speaking in the terms of performance)?

P.S. Let me know if something isn't clear.

Edit: I guess I don't know what the hell I'm doing regarding Singletons, but to make it clear, this is a newbie question regarding performance:

1) _audioManager = AudioManager.audioManager in Awake and then use _audioManager.someVariable in Update

VS

2) AudioManager.audioManager.someVariable in Update

Upvotes: 0

Views: 1431

Answers (2)

theodox
theodox

Reputation: 12218

You don't need to cache your own component for speed in any case. A component won't pay a performance penalty for accessing itself.

The use case you may be thinking of is caching access to other components or objects so that you don't have to do lookup with GetComponent or GetComponentWithChildren. That does save time, but it's not a complex pattern: just do the lookup the first time you encounter the other component or gameobject, store it in a field inside this component, and you're good:

 class Example: MonoBehavior
 {

     public GameObject _TargetCharacter;
     private Transform _cachedTransform;
     private Animator  _cachedAnimator;

     void Awake()
     {
      if (_TargetCharacter != null)
      {
          _cachedTransform = _TargetCharacter.transform;
          _cachedAnimator = _TargetCharacter.GetComponent<Animator>();
       }
     }


     void Update()
     {
         DoSomething (_cachedTransform, _cachedAnimator);
     }
 } 

GetComponent and GetComponentInChildren are comparatively expensive you do don't want to do them every frame. However direct access to something set as a field is not expensive there is no need to cache it.

And making it s Singleton: in this context, you would need to make a separate class which is NOT a Component and have that be the singleton - you can't prevent somebody from attaching lots of a given MonoBehavior to things in the Unity Editor so you don't want to assume there's only one of the component floating around. In the AudioManager example, you'd have to assume that many different components might all be looking at the same AudioManager.

Here a good example comparing shared behavior using Singletons vs shared behavior in a static class. Two means to the same end, with slightly different strengths and weaknesses.

Update In response to OP's comment below:

If the point of the original code is to gain speed using the common Unity caching trick, it's not needed. If the point is, as OP suggests in comment below, to make the manager into a 'service provider' that other classes can call there are two routes to go:

The standard unity trick would be to add the caching behavior to the other classes that need to get to the AudioManager. For example:

class SomeOtherComponent: MonoBehavior
{
    AudioManager _manager;
    void Awake()
    {
    AudioManager[] AllAudioManagers = GetComponents<AudioManager>();
    if (AllAudioManagers.Count == 1)
        {
        _manager = AllAudioManagers[0];
        }
    else
        {
         throw new RuntimeError ("Expecting one (and only one) AudioManager in scene");
        } 
    }
}

Switching to a proper Singleton implementation more or less amounts to just taking the boilerplate in the Awake method above and centralizing into the singleton class, allowing you to replace it.

For an extended discussion of the singleton pattern in unity try this book

Upvotes: 2

axwcode
axwcode

Reputation: 7824

I live by a simple rule, if I am going to be calling a component a lot of times, more than 5, then I cache it. The same rule I apply to Singletons. If it is only one call then I just use the Singleton.getInstance().


What you should be really caching

Unity Components such as Transform.

The logic behind caching things like Transform comes from looking at the documentation. Transform is not a variable but a property, which happens to contain getters and setters. These are hidden from you, but that's the way it is implemented. This property happens to be written in C or C++. Therefore this will cost you on performance.

Upvotes: 0

Related Questions