No, that’s just not true. Are you sure you don’t combined your second test case with your thrid where you set the variable to null yourself? Breaking out of a coroutine or just letting it end has no effect on a variable that is holding a Coroutine object.
Some basics: In C# variables NEVER magically turn null. UnityEngine.Object derived types SEEMS to be able to do that, but they actually don’t. In C# objects can never actively be destroyed. It’s always the garbage collector that eventually destroys managed objects once there’s no reference to that object left. UnityEngine.Object derived types have an internal native code representation on the native C++ side. When you use Destroy on such an object you actually destroy the object on the C++ side. However the managed C# wrapper object sill exists and is still referenced by the same variables. However once the native object is gone, the managed UnityEngine.Object will “pretend” to be null since it has a custom == operator which will simply make such a destroyed object to appear null.
However coroutines (Coroutine and IEnumerator objects) are not UnityEngine.Object derived classes. They are just ordinary managed C# classes and as such can never magically turn null.
The underlying class that implements the IEnumerator interface is actually a statemachine. The fact that the statemachine reached its endstate doesn’t make the class go away. Unity (the coroutine scheduler to be more precise) of course stores the IEnumerator internally and keep “iterating” it until the coroutine finishes. Once the coroutine is finshed, Unity will no longer hold reference internally to the Coroutine or the IEnumerator as it is done with it. However when you hold that instance in your own variable, it would stay there useless forever.
Your variable can only become null when you set it to null.
In any cases where you have a variable that does “strange” or unexpected things, I always recommend to just replace the variable with a property instead. That way you can actually add Debug.Log statements inside the setter of the property so you know where and when the content of your variable / property gets changed. However since your variable seems to be a private variable, the only one that could change the variable (under normal circumstances) is the class itself. So unless you use “bad black magic” (aka reflection) in some weird way, the variable won’t turn null on its own.
If you’re interested in some details how coroutines actually work under the hood, here’s my coroutine crash course (mirror on github).
ps: I think I should directly address your two questions that you numbered.
- What you’re doing here is not caching the coroutine but simply storing it. The term caching is only used when we store something that is no longer in use so we can reuse it later. You usually store a running coroutine in order to interact with it, specifically to be able to stop it which is exactly what you’re doing. So yes, doing this is totally fine.
- I’m not sure what you mean by “correct”. In your first case you only allow the starting method to start a new coroutine when the variable which holds the old Coroutine instance is specifically set to null. If you only set your variable to null in your stop method it means you can not start a new coroutine unless you specifically called the stop method. So the coroutine could be finished but your variable of course still holds the reference and would prevent the coroutine from being started again. If that’s what you want, sure your first case is “correct”.
In your second case the coroutine itself sets its own variable to null when it’s done. You can do that as well. In that caes the variable would actually be set to null once the coroutine is finished.
As I already said, your third case just isn’t true. Breaking out of a coroutine does not change any variables. Using yield break or letting the coroutine finish has the exact same effect. Namely the iterator will reach its final state.