I just spent a bunch of time trying to track down a dangling listener. It would be really nice if juce automatically managed the listener connection so it would be impossible, kind of like Qt’s signals / slots. But I’m guessing that a pretty major API change.
Instead, how about something like this (in debug builds only):
diff --git a/modules/juce_data_structures/values/juce_ValueTree.cpp b/modules/juce_data_structures/values/juce_ValueTree.cpp
index e185d8d81..7675e1ed4 100644
--- a/modules/juce_data_structures/values/juce_ValueTree.cpp
+++ b/modules/juce_data_structures/values/juce_ValueTree.cpp
@@ -638,7 +638,10 @@ ValueTree::ValueTree (ValueTree&& other) noexcept
}
ValueTree::~ValueTree()
-{
+{
+ for (int i = 0; i < listeners.size(); i++)
+ listeners.getListeners().getUnchecked (i)->count--;
+
if (! listeners.isEmpty() && object != nullptr)
object->valueTreesWithListeners.removeValue (this);
}
@@ -969,7 +972,10 @@ void ValueTree::reorderChildren (const OwnedArray<ValueTree>& newOrder, UndoMana
void ValueTree::addListener (Listener* listener)
{
if (listener != nullptr)
- {
+ {
+ if (! listeners.getListeners().contains (listener))
+ listener->count++;
+
if (listeners.isEmpty() && object != nullptr)
object->valueTreesWithListeners.add (this);
@@ -978,9 +984,12 @@ void ValueTree::addListener (Listener* listener)
}
void ValueTree::removeListener (Listener* listener)
-{
- listeners.remove (listener);
+{
+ if (listeners.getListeners().contains (listener))
+ listener->count--;
+ listeners.remove (listener);
+
if (listeners.isEmpty() && object != nullptr)
object->valueTreesWithListeners.removeValue (this);
}
diff --git a/modules/juce_data_structures/values/juce_ValueTree.h b/modules/juce_data_structures/values/juce_ValueTree.h
index 1145a68ae..652391d54 100644
--- a/modules/juce_data_structures/values/juce_ValueTree.h
+++ b/modules/juce_data_structures/values/juce_ValueTree.h
@@ -481,8 +481,13 @@ public:
{
public:
/** Destructor. */
- virtual ~Listener() = default;
-
+ virtual ~Listener()
+ {
+ jassert (count == 0);
+ }
+
+ int count = 0;
+
/** This method is called when a property of this tree (or of one of its sub-trees) is changed.
Note that when you register a listener to a tree, it will receive this callback for
property changes in that tree, and also for any of its children, (recursively, at any depth).
I added this and it tracked down my listener leak in < 10 seconds.