[Interest] Handling QObject auto-deletion and signals emitted from destructors

Neil Williams neil at copycopy.cc
Fri Sep 4 12:39:35 CEST 2015


Hi Bo,
Thanks for your feedback:

On Fri, Sep 4, 2015 at 8:29 AM, Bo Thorsen <bo at vikingsoft.eu> wrote:

> If your question is only about the generic question and not about the
> code, skip to the bottom of my reply. If it's about the actual code you
> posted, keep reading :)
>
> Your problem is that you call a method on an object that's deleted. In
> this code:
>
> connect(webview, &QWebView::destroyed, [this] () {
>    // replicate loadFinished being fired during qwebview/webpage
> destruction
>    emit webview->loadFinished(false);
> });
>
> This is completely wrong. When destroyed is emitted, the QWebView object
> is already "deleted" (i.e. the destructor has been called) and only the
> QObject part of it remain. Calling a method on such a half object is
> doomed.
>
> I don't understand why you don't just call onLoadFinished(whatever)
> instead of trying to replicate anything. This is what you need to do.
>
>
Emitting from the destroyed signal here was just me trying to replicate the
loadFinished signal being emitted from the QWebViewdestructor if a page
is loading, without actually having to load a page.

This is a chunk of a call stack showing how the loadFinished signal is
emitted
from QWebView during destruction:

     Qt5WebKit.dll!WebCore::FrameLoaderClientQt::dispatchDidFailLoad(const
WebCore::ResourceError & error={...}) Line 1191    C++
>    Qt5WebKit.dll!WebCore::ResourceLoader::cancel(const
WebCore::ResourceError & error={...}) Line 431    C++
     Qt5WebKit.dll!WebCore::ResourceLoader::cancel() Line 385    C++
     Qt5WebKit.dll!WebCore::cancelAll(const
WTF::HashSet<WTF::RefPtr<WebCore::ResourceLoader>,WTF::PtrHash<WTF::RefPtr<WebCore::ResourceLoader>
>,WTF::HashTraits<WTF::RefPtr<WebCore::ResourceLoader> > > & loaders={...})
Line 85    C++
     Qt5WebKit.dll!WebCore::DocumentLoader::stopLoading() Line 314    C++

 Qt5WebKit.dll!WebCore::FrameLoader::stopAllLoaders(WebCore::ClearProvisionalItemPolicy
clearProvisionalItemPolicy=ShouldClearProvisionalItem) Line 1578    C++
     Qt5WebKit.dll!WebCore::FrameLoader::detachFromParent() Line 2413    C++
     Qt5WebKit.dll!QWebPageAdapter::deletePage() Line 308    C++
     Qt5WebKitWidgets.dll!QWebPage::`vector deleting destructor'(unsigned
int)    C++
     Qt5WebKitWidgets.dll!QWebViewPrivate::detachCurrentPage() Line 236
 C++
     Qt5WebKitWidgets.dll!QWebViewPrivate::`scalar deleting
destructor'(unsigned int)    C++
     Qt5WebKitWidgets.dll!QWebView::~QWebView() Line 200    C++

Of course it isn't emitted at the same point in my demo app in the original
message
but the important thing is that at the point the signal is emitted, the
sibling widgets
(ok_widget and fail_widget) are already destroyed.

The emitting from the destroyed signal as in the demo app is not something
I would
normally write.

There are some things you can do to an object in a slot that's direct
> connected to the destroyed signal. But it's a good general rule for
> anyone that you just can't do anything to it. Unless you really know
> what you are doing, you are going to get into trouble with this. And I
> don't think I've ever needed to do this. Accept that the object is gone
> and fix the code to handle this.
>
> Another thing with your code: Why do you even want to do this? If the
> web view is destroyed, that means the entire widget is destroyed. So why
> do you need to set anything on the other widgets?
>
>

> And now for the general answer: You are in trouble here. Yes, a QPointer
> is the way to track if an object is deleted or not, and I don't see how
> that's tedious. However, when you are mixing a slot connected to
> destroyed and QPointers, you start depending on the order of slots
> called. To avoid this, I'd use a queued signal or handle it in some
> other way. For this, we need actual code to help.
>
> Bo.
>
> Den 01-09-2015 kl. 17:06 skrev Neil Williams:
> > Below is a example program which will crash when the widget is closed.
> >
> > This setup replicates the QWebview behaviour where it will emit the
> > loadFinished signal from inside the destructor if a page is loading.
> >
> > I am wondering if anyone can suggest a clean way of handling this sort
> > of case, where a sibling may have been deleted but is referenced in a
> > connected slot of a non-deleted object.
> >
> > I have considered:
> >
> > 1. Always using QPointer and check for object validity before accessing
> > in the slot. This seems like it would be somewhat tedious.
> >
> > 2. Using the 'context' param of QObject::connect override and splitting
> > the current slot into multiple smaller slots that only access a single
> > param, e.g:
> >
> > connect(webview,&QWebView::loadFinished, ok_widget, [this] (boolok) {
> >
> > ok_widget->setVisible(ok);
> >
> > });
> >
> >
> > connect(webview,&QWebView::loadFinished, fail_widget, [this] (boolok) {
> >
> > fail_widget->setVisible(!ok);
> >
> > });
> >
> >
> > This requires more boilerplate and may not be possible for more complex
> > slots which need to access multiple objects.
> >
> >
> > 3. Call to QObject::disconnect in the containing class destructor. This
> > does feel a little bit like I am micro-managing the connections which
> > would be nice to avoid.
> >
> >
> > 4. Switch to manual memory management of the widgets so I can control
> > destruction order.
> >
> >
> > At the moment I am considering either 1, 3 or 4 but I would appreciate
> > any other views/opinions/suggestions.
> >
> > Thanks.
> >
> > #ifndefWIDGET_H
> >
> > #defineWIDGET_H
> >
> >
> > #include<QDebug>
> >
> > #include<QtWidgets>
> >
> > #include<QWebView>
> >
> > #include<QPointer>
> >
> >
> > classQWebView;
> >
> >
> > classWidget:publicQWidget
> >
> > {
> >
> > Q_OBJECT
> >
> >
> > public:
> >
> > explicitWidget(){
> >
> >
> > QVBoxLayout*layout=newQVBoxLayout();
> >
> >
> > webview=newQWebView();
> >
> > ok_widget=newQWidget();
> >
> > fail_widget=newQWidget();
> >
> >
> > connect(webview,&QWebView::destroyed,[this](){
> >
> > //replicateloadFinishedbeingfiredduringqwebview/webpagedestruction
> >
> > emitwebview->loadFinished(false);
> >
> > });
> >
> >
> > connect(webview,&QWebView::loadFinished,this,&Widget::onLoadFinished);
> >
> >
> > layout->addWidget(ok_widget);
> >
> > layout->addWidget(fail_widget);
> >
> > layout->addWidget(webview);
> >
> >
> > setLayout(layout);
> >
> > }
> >
> >
> > ~Widget(){
> >
> > qDebug()<<"Windowdestroyed";
> >
> > }
> >
> >
> > privateslots:
> >
> > voidonLoadFinished(boolok){
> >
> > ok_widget->setVisible(ok);
> >
> > fail_widget->setVisible(!ok);
> >
> > }
> >
> >
> > private:
> >
> > QWebView*webview;
> >
> > QWidget*ok_widget;
> >
> > QWidget*fail_widget;
> >
> >
> > };
> >
> >
> > #endif//WIDGET_H
> >
> >
> >
> >
> > _______________________________________________
> > Interest mailing list
> > Interest at qt-project.org
> > http://lists.qt-project.org/mailman/listinfo/interest
> >
>
> Bo Thorsen,
> Director, Viking Software.
>
> --
> Viking Software
> Qt and C++ developers for hire
> http://www.vikingsoft.eu
> _______________________________________________
> Interest mailing list
> Interest at qt-project.org
> http://lists.qt-project.org/mailman/listinfo/interest
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.qt-project.org/pipermail/interest/attachments/20150904/98d8c45c/attachment.html>


More information about the Interest mailing list