Answering my own question.
Instead,  my client app 
- saves a list of listeners that link to it.
- listens its ObjectBeingDeleted event, as this is more consistent than the CloseRequest callback
- deletes the listeners within its ObjectBeingDeletedCallback
as per this code snippet
    properties (Access = private)
        listenerList (1,:) event.listener
    end
    methods (Access = public)
        .
        .
        .
        function sourceChangedCallback( app, src, evdata )
            % do useful things...
        end
        function addSourceChangedListener( app, src, eventName )
            app.listenerList(end+1) = addlistener(src, eventName, @app.sourceChangedCallback);
        end
    end
    methods( Access = private)
        function beingDestroyedCallback( app, src, evdata ) %#ok<INUSD> 
            app.deleteListeners();    
        end
        function deleteListeners( app )
            delete( app.listenerList );
            app.listenerList = event.listener.empty();
        end
    end
    .
    .
    .
    % Code that executes after component creation
    function startupFcn(app)
        addlistener(app, 'ObjectBeingDestroyed', ...
                         @app.beingDestroyedCallback);
    end

