diff --git a/lib/matplotlib/backends/backend_macosx.py b/lib/matplotlib/backends/backend_macosx.py index e914ea7ae7f6..204f81fd098b 100644 --- a/lib/matplotlib/backends/backend_macosx.py +++ b/lib/matplotlib/backends/backend_macosx.py @@ -37,6 +37,8 @@ def __init__(self, figure): super().__init__(figure=figure) self._draw_pending = False self._is_drawing = False + # Keep track of the timers that are alive + self._timers = set() def draw(self): """Render the figure and update the macosx canvas.""" @@ -59,14 +61,16 @@ def draw_idle(self): def _single_shot_timer(self, callback): """Add a single shot timer with the given callback""" - # We need to explicitly stop (called from delete) the timer after + # We need to explicitly stop and remove the timer after # firing, otherwise segfaults will occur when trying to deallocate # the singleshot timers. def callback_func(callback, timer): callback() - del timer + self._timers.remove(timer) + timer.stop() timer = self.new_timer(interval=0) timer.add_callback(callback_func, callback, timer) + self._timers.add(timer) timer.start() def _draw_idle(self): @@ -150,6 +154,14 @@ def _close_button_pressed(self): Gcf.destroy(self) self.canvas.flush_events() + def destroy(self): + # We need to clear any pending timers that never fired, otherwise + # we get a memory leak from the timer callbacks holding a reference + while self.canvas._timers: + timer = self.canvas._timers.pop() + timer.stop() + super().destroy() + @classmethod def start_main_loop(cls): _macosx.show() diff --git a/src/_macosx.m b/src/_macosx.m index 30fdf8fbd75c..d31758b95483 100755 --- a/src/_macosx.m +++ b/src/_macosx.m @@ -193,7 +193,6 @@ @interface Window : NSWindow - (Window*)initWithContentRect:(NSRect)rect styleMask:(unsigned int)mask backing:(NSBackingStoreType)bufferingType defer:(BOOL)deferCreation withManager: (PyObject*)theManager; - (NSRect)constrainFrameRect:(NSRect)rect toScreen:(NSScreen*)screen; - (BOOL)closeButtonPressed; -- (void)dealloc; @end @interface View : NSView @@ -1239,19 +1238,9 @@ - (void)close /* This is needed for show(), which should exit from [NSApp run] * after all windows are closed. */ -} - -- (void)dealloc -{ - PyGILState_STATE gstate; - gstate = PyGILState_Ensure(); + // For each new window, we have incremented the manager reference, so + // we need to bring that down during close and not just dealloc. Py_DECREF(manager); - PyGILState_Release(gstate); - /* The reference count of the view that was added as a subview to the - * content view of this window was increased during the call to addSubview, - * and is decreased during the call to [super dealloc]. - */ - [super dealloc]; } @end