From 9f2249197d16cbbdcdf2d621d57c474fc1299e80 Mon Sep 17 00:00:00 2001 From: Tal Leming Date: Wed, 20 Dec 2023 12:33:12 -0500 Subject: [PATCH 1/4] Use respondsToSelector instead of hasattr in _breakCycles. This addresses #145. Closing the test window in the issue goes from taking 0.11 seconds to 0.01 seconds. --- Lib/vanilla/vanillaBase.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Lib/vanilla/vanillaBase.py b/Lib/vanilla/vanillaBase.py index a14d15d..bf3800d 100644 --- a/Lib/vanilla/vanillaBase.py +++ b/Lib/vanilla/vanillaBase.py @@ -543,9 +543,8 @@ def _breakCycles(view): """ Break cyclic references by deleting _target attributes. """ - if hasattr(view, "vanillaWrapper"): + if view.respondsToSelector_("vanillaWrapper"): obj = view.vanillaWrapper() - if hasattr(obj, "_breakCycles"): - obj._breakCycles() + obj._breakCycles() for view in view.subviews(): _breakCycles(view) From d2c4d47a0c89c6395e3280d3b4dfcc7ad0d0f14e Mon Sep 17 00:00:00 2001 From: Tal Leming Date: Fri, 24 Jan 2025 11:34:15 -0500 Subject: [PATCH 2/4] Test for None. Suggested by @typemytype in #200. --- Lib/vanilla/vanillaBase.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Lib/vanilla/vanillaBase.py b/Lib/vanilla/vanillaBase.py index bf3800d..a836a64 100644 --- a/Lib/vanilla/vanillaBase.py +++ b/Lib/vanilla/vanillaBase.py @@ -545,6 +545,7 @@ def _breakCycles(view): """ if view.respondsToSelector_("vanillaWrapper"): obj = view.vanillaWrapper() - obj._breakCycles() + if obj is not None: + obj._breakCycles() for view in view.subviews(): _breakCycles(view) From 0f48bd1c3dc6499a298873bbe4ace6b55a225311 Mon Sep 17 00:00:00 2001 From: Tal Leming Date: Fri, 24 Jan 2025 16:42:09 -0500 Subject: [PATCH 3/4] Handle a couple of situations. --- Lib/vanilla/vanillaBase.py | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Lib/vanilla/vanillaBase.py b/Lib/vanilla/vanillaBase.py index a836a64..7adde96 100644 --- a/Lib/vanilla/vanillaBase.py +++ b/Lib/vanilla/vanillaBase.py @@ -545,7 +545,22 @@ def _breakCycles(view): """ if view.respondsToSelector_("vanillaWrapper"): obj = view.vanillaWrapper() - if obj is not None: + if obj is None: + pass + # some of the objects that arrive here, + # defconAppKit's BaseWindowController + # for example, don't have a _breakCycles + # method and will raise an error. we can't + # do a hasattr(obj, "_breakCycles") on all + # objects because NSObjects will arrive here + # and hasattr on NSObject is very expensive. + # so, go ahead and call _breakCycles on + # NSObject, but test for the method on + # other objects. this is convoluted, but + # there doesn't seem to be a way around it. + elif isinstance(obj, NSObject): + obj._breakCycles() + elif hasattr(obj, "_breakCycles"): obj._breakCycles() for view in view.subviews(): _breakCycles(view) From 1ab15a2ced71a183624f65933c95cc8145feeebd Mon Sep 17 00:00:00 2001 From: Tal Leming Date: Fri, 24 Jan 2025 20:04:20 -0500 Subject: [PATCH 4/4] The NSObject test shouldn't be necessary. I don't know why any NSObject would be set as "vanillaWrapper" so remove this test. --- Lib/vanilla/vanillaBase.py | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/Lib/vanilla/vanillaBase.py b/Lib/vanilla/vanillaBase.py index 7adde96..6fe64ad 100644 --- a/Lib/vanilla/vanillaBase.py +++ b/Lib/vanilla/vanillaBase.py @@ -550,17 +550,8 @@ def _breakCycles(view): # some of the objects that arrive here, # defconAppKit's BaseWindowController # for example, don't have a _breakCycles - # method and will raise an error. we can't - # do a hasattr(obj, "_breakCycles") on all - # objects because NSObjects will arrive here - # and hasattr on NSObject is very expensive. - # so, go ahead and call _breakCycles on - # NSObject, but test for the method on - # other objects. this is convoluted, but - # there doesn't seem to be a way around it. - elif isinstance(obj, NSObject): - obj._breakCycles() - elif hasattr(obj, "_breakCycles"): + # method and will raise an error. + if hasattr(obj, "_breakCycles"): obj._breakCycles() for view in view.subviews(): _breakCycles(view)