Even though I’m careful to call removeActionListener for every time I call addActionListener.
This is on a Windows 11 64-bit executable in debug mode using Juce version 7.5. It works in release mode, but I don’t really trust it.
The assertion I’m getting is as follows:
ActionBroadcaster::~ActionBroadcaster()
{
// all event-based objects must be deleted BEFORE juce is shut down!
JUCE_ASSERT_MESSAGE_MANAGER_EXISTS
}
Am I doing something wrong or is it a bug?
Here’s the minimum code to reproduce the problem:
GlobalState.h
#pragma once
#include <JuceHeader.h>
// ========================================================================================================================
/*
* Implements a singleton that provides a place for global variables and optionally, an event to notify other
* components of changes.
*/
class GlobalState : public juce::ActionBroadcaster
{
public:
static GlobalState& Instance()
{
static GlobalState S;
return S;
}
int getCurrentBeat() const;
void setCurrentBeat(int CurrentBeat);
int getLoopCount();
private:
GlobalState();
~GlobalState();
// ========================================================================================================================
int currentBeat{ 4 }; // The current beat of the metronome.
int loopCount{ 0 };
};
GlobalState.cpp
#include "GlobalState.h"
GlobalState::GlobalState()
{
}
GlobalState::~GlobalState()
{
}
int GlobalState::getCurrentBeat() const
{
return currentBeat;
}
void GlobalState::setCurrentBeat(int CurrentBeat)
{
currentBeat = CurrentBeat;
sendActionMessage("GlobalState:DownBeat");
}
int GlobalState::getLoopCount()
{
return loopCount;
}
MainComponent.h
#pragma once
#include <JuceHeader.h>
#include "GlobalState.h"
//==============================================================================
/*
This component lives inside our window, and this is where you should put all
your controls and content.
*/
class MainComponent : public juce::Component, public juce::ActionListener
{
public:
//==============================================================================
MainComponent();
~MainComponent() override;
//==============================================================================
void paint (juce::Graphics&) override;
void resized() override;
void actionListenerCallback(const juce::String& message) override;
private:
//==============================================================================
GlobalState& globalState = GlobalState::Instance();
juce::TextButton buttonQuit{ "Quit" };
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (MainComponent)
};
MainComponen.cpp
#include "MainComponent.h"
//==============================================================================
MainComponent::MainComponent()
{
addAndMakeVisible(buttonQuit);
buttonQuit.onClick = [this] { juce::JUCEApplication::quit(); };
globalState.addActionListener(this);
setSize (600, 400);
}
MainComponent::~MainComponent()
{
globalState.removeActionListener(this);
}
//==============================================================================
void MainComponent::paint (juce::Graphics& g)
{
// (Our component is opaque, so we must completely fill the background with a solid colour)
g.fillAll (getLookAndFeel().findColour (juce::ResizableWindow::backgroundColourId));
g.setFont (juce::Font (16.0f));
g.setColour (juce::Colours::white);
g.drawText ("Hello World!", getLocalBounds(), juce::Justification::centred, true);
}
void MainComponent::resized()
{
buttonQuit.setBounds(getLocalBounds().getWidth() / 2, 8, 60, 30);
}
void MainComponent::actionListenerCallback(const juce::String& message)
{
if (message == "GlobalState:DownBeat")
DBG("MainComponent::actionListenerCallback");
}