ManagedCallable: use delegate target instead of middleman when possible

If the delegate target is an Object, the connected signal will be registered in that object instead of the middleman. So when that object is destroyed, the signal will be properly disconnected.
This commit is contained in:
Patrick Dawson 2022-09-30 18:22:11 +02:00
parent e69b7083d4
commit 161f295f52
6 changed files with 13 additions and 8 deletions

View file

@ -72,7 +72,7 @@ namespace Godot
/// <param name="delegate">Delegate method that will be called.</param>
public Callable(Delegate @delegate)
{
_target = null;
_target = @delegate?.Target as Object;
_method = null;
_delegate = @delegate;
}

View file

@ -721,8 +721,9 @@ namespace Godot.NativeInterop
if (p_managed_callable.Delegate != null)
{
var gcHandle = CustomGCHandle.AllocStrong(p_managed_callable.Delegate);
IntPtr objectPtr = p_managed_callable.Target != null ? Object.GetPtr(p_managed_callable.Target) : IntPtr.Zero;
NativeFuncs.godotsharp_callable_new_with_delegate(
GCHandle.ToIntPtr(gcHandle), out godot_callable callable);
GCHandle.ToIntPtr(gcHandle), objectPtr, out godot_callable callable);
return callable;
}
else

View file

@ -141,7 +141,7 @@ namespace Godot.NativeInterop
public static partial void godotsharp_packed_string_array_add(ref godot_packed_string_array r_dest,
in godot_string p_element);
public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle,
public static partial void godotsharp_callable_new_with_delegate(IntPtr p_delegate_handle, IntPtr p_object,
out godot_callable r_callable);
internal static partial godot_bool godotsharp_callable_get_data_for_marshalling(in godot_callable p_callable,

View file

@ -447,9 +447,10 @@ void godotsharp_packed_string_array_add(PackedStringArray *r_dest, const String
r_dest->append(*p_element);
}
void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, Callable *r_callable) {
void godotsharp_callable_new_with_delegate(GCHandleIntPtr p_delegate_handle, const Object *p_object, Callable *r_callable) {
// TODO: Use pooling for ManagedCallable instances.
CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle));
ObjectID objid = p_object ? p_object->get_instance_id() : ObjectID();
CallableCustom *managed_callable = memnew(ManagedCallable(p_delegate_handle, objid));
memnew_placement(r_callable, Callable(managed_callable));
}

View file

@ -79,7 +79,9 @@ CallableCustom::CompareLessFunc ManagedCallable::get_compare_less_func() const {
}
ObjectID ManagedCallable::get_object() const {
// TODO: If the delegate target extends Godot.Object, use that instead!
if (object_id != ObjectID()) {
return object_id;
}
return CSharpLanguage::get_singleton()->get_managed_callable_middleman()->get_instance_id();
}
@ -104,7 +106,7 @@ void ManagedCallable::release_delegate_handle() {
// Why you do this clang-format...
/* clang-format off */
ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle) : delegate_handle(p_delegate_handle) {
ManagedCallable::ManagedCallable(GCHandleIntPtr p_delegate_handle, ObjectID p_object_id) : delegate_handle(p_delegate_handle), object_id(p_object_id) {
#ifdef GD_MONO_HOT_RELOAD
{
MutexLock lock(instances_mutex);

View file

@ -40,6 +40,7 @@
class ManagedCallable : public CallableCustom {
friend class CSharpLanguage;
GCHandleIntPtr delegate_handle;
ObjectID object_id;
#ifdef GD_MONO_HOT_RELOAD
SelfList<ManagedCallable> self_instance = this;
@ -66,7 +67,7 @@ public:
void release_delegate_handle();
ManagedCallable(GCHandleIntPtr p_delegate_handle);
ManagedCallable(GCHandleIntPtr p_delegate_handle, ObjectID p_object_id);
~ManagedCallable();
};