Quick answer: A method(inst, func) callback holds the instance — when that instance is destroyed, calling it throws. Bind to a long-lived struct, or check instance_exists at the call site.
A time source callback bound to an instance fires after the instance is destroyed and throws “Reference is not a valid instance”.
method Binds Lifetime
method(inst, fn) stores both the function and the bound “self”. When the time source fires, it tries to switch context to inst — if it’s gone, error.
Guard at Call Site
cb = method(self, function() {
if (!instance_exists(self)) return;
// real work
});
Cheap check, prevents the throw. Use whenever the callback might outlive the bound instance.
Bind to a Struct
For callbacks that should genuinely outlive any instance, bind to a struct that lives as long as the system:
global.callbacks = {};
method(global.callbacks, function() { ... });
Structs aren’t instances — no “destroyed” concept — so the callback always has a valid self.
Cancel on Destroy
For per-instance callbacks (a time source created in Create), cancel them in Clean Up. That’s the cleanest fix — the callback never fires after the owner’s gone.
Verifying
Spawn instances that register callbacks, destroy them mid-flight. No reference errors. Callbacks that legitimately need to fire after destruction land on a struct instead.
“method binds an instance. If the instance can die before the call, guard, cancel, or bind to a struct.”
A small ‘cancel all my callbacks’ helper in Clean Up beats hunting them one by one — track time-source IDs in an array per instance.