Skip to content
  • Categories
  • Recent
  • Tags
  • Popular
  • Users
  • Groups
  • Search
  • Get Qt Extensions
  • Unsolved
Collapse
Brand Logo
  1. Home
  2. Qt Development
  3. General and Desktop
  4. Real confusion about when to delete QNetworkReply object
Forum Updated to NodeBB v4.3 + New Features

Real confusion about when to delete QNetworkReply object

Scheduled Pinned Locked Moved Solved General and Desktop
31 Posts 7 Posters 3.2k Views 5 Watching
  • Oldest to Newest
  • Newest to Oldest
  • Most Votes
Reply
  • Reply as topic
Log in to reply
This topic has been deleted. Only users with topic management privileges can see it.
  • S Offline
    S Offline
    SGaist
    Lifetime Qt Champion
    wrote on 21 Jul 2021, 15:01 last edited by
    #7

    What stack trace do you get when it crashes ?

    Interested in AI ? www.idiap.ch
    Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

    H 1 Reply Last reply 21 Jul 2021, 16:26
    0
    • S SGaist
      21 Jul 2021, 15:01

      What stack trace do you get when it crashes ?

      H Offline
      H Offline
      hbatalha
      wrote on 21 Jul 2021, 16:26 last edited by
      #8

      @SGaist I get this
      Screenshot_1.png

      Which points to this line in the closeEvent ;

       if(reply != nullptr && downloadBegan && ! reply->isFinished())
      

      This is the connect slot used:

      QObject::connect(&net, &QNetworkAccessManager::finished, [this, dest_file, fileDownloadRequestType](QNetworkReply* reply)
          {
              if(! reply->error())
              {
                  QByteArray data = reply->readAll();
                  reply->deleteLater();
      
                  QFile file(dest_file);
      
                  if( ! file.open(QIODevice::ReadWrite | QIODevice::Truncate))
                      Util::displayErrorMessage(tr("Something went wrong"), tr("Could not save the file"), this, true);
      
                  file.write(data);
                  file.close();
      
                  ui->msg_label->setText(tr("Download Complete"));
                  ui->progress_label->setText("");
      
                  finished = true;
      
                  exe = dest_file;
      
                  if(fileDownloadRequestType == FileDownloadRequestType::UPDATE)
                      handleUpdateDownloadFinished();
              }
          });
      

      The difference here I call deleteLater right after QByteArray data = reply->readAll();

      1 Reply Last reply
      0
      • S Offline
        S Offline
        SGaist
        Lifetime Qt Champion
        wrote on 21 Jul 2021, 18:01 last edited by
        #9

        You know that deleteLater does not change that pointer to a nullptr value ?

        Interested in AI ? www.idiap.ch
        Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

        H 1 Reply Last reply 21 Jul 2021, 21:34
        1
        • J Offline
          J Offline
          JoeCFD
          wrote on 21 Jul 2021, 18:25 last edited by JoeCFD
          #10

          Use this to check if reply finishes before it is deleted.
          connect( m_reply, &QNetworkReply::finished, **** );

          Normally, you may do the following because it may finish immediately and the slot may not be called. Sigh!

             if ( nullptr != m_reply ) { /* check reply is null or not first */
                 if ( m_reply->isRunning() ) { /* connect when it is still running */
                     connect( m_reply, &QNetworkReply::finished,
                              this,    &getFinished() );
                 }
                 else { /* not running(already finished) and call slot directly while connection is not needed */
                     getFinished();
                 }
             }
          
          H J 2 Replies Last reply 21 Jul 2021, 21:43
          0
          • S SGaist
            21 Jul 2021, 18:01

            You know that deleteLater does not change that pointer to a nullptr value ?

            H Offline
            H Offline
            hbatalha
            wrote on 21 Jul 2021, 21:34 last edited by
            #11

            @SGaist said in Real confusion about when to delete QNetworkReply object:

            You know that deleteLater does not change that pointer to a nullptr value ?

            Yeah, which is making me confused on why it's crashing the app. Any ideas?

            I made some changes in these two functions:

            void FileDownloader::download(QString url, QString dest_file, QString downloadName, FileDownloadRequestType fileDownloadRequestType)
            {
                this->show();
            
                downloadBegan = false;
                finished = false;
            
                connect(ui->pushButton, &QPushButton::clicked, [this] { this->close(); });
            
                if(MAIN.preferences.theme == Preferences::Theme::DARK)
                {
                    ui->msg_label->setStyleSheet("color : white;");
                    ui->progress_label->setStyleSheet("color : white;");
                }
                else  if(MAIN.preferences.theme == Preferences::Theme::LIGHT)
                {
                    ui->msg_label->setStyleSheet("color : black;");
                    ui->progress_label->setStyleSheet("color : black;");
                }
            
                ui->download_progressBar->setMinimum(0);
                ui->download_progressBar->setMaximum(0);
            
                ui->msg_label->setText(tr("Downloading ") + downloadName);
            
                reply = net.get(QNetworkRequest(QUrl(url)));
            
                downloadBegan = true;
                finished = false;
            
                if(QFile::exists(dest_file))
                    QFile::remove(dest_file);
            
                QObject::connect(reply, &QNetworkReply::downloadProgress, [this](qint64 received, qint64 total)
                {
                    qDebug() << received << " of " << total;
            
                    ui->progress_label->setText(Util::displaySize(received) + " of " + Util::displaySize(total));
            
                    ui->download_progressBar->setRange(0, total);
                    ui->download_progressBar->setValue(received);
                });
            
                QObject::connect(&net, &QNetworkAccessManager::finished, [this, dest_file, fileDownloadRequestType](QNetworkReply* reply)
                {
                    finished = true;
            
                    if(! reply->error())
                    {
                        QByteArray data = reply->readAll();
                        reply->deleteLater();
            
                        QFile file(dest_file);
            
                        if( ! file.open(QIODevice::ReadWrite | QIODevice::Truncate))
                            Util::displayErrorMessage(tr("Something went wrong"), tr("Could not save the file"), this, true);
            
                        qDebug() << "Size written: " <<  file.write(data);
                        file.close();
            
                        ui->msg_label->setText(tr("Download Complete"));
                        ui->progress_label->setText("");
            
                        exe = dest_file;
            
                        if(fileDownloadRequestType == FileDownloadRequestType::UPDATE)
                            handleUpdateDownloadFinished();
                    }
                });
            }
            
            void FileDownloader::closeEvent(QCloseEvent *)
            {
                if( downloadBegan && ! finished)
                    reply->abort();
            }
            

            Instead of using the reply object to check if the download has finished I am using bool variables. I call deleteLater in the slot.

            J 1 Reply Last reply 21 Jul 2021, 23:55
            0
            • J JoeCFD
              21 Jul 2021, 18:25

              Use this to check if reply finishes before it is deleted.
              connect( m_reply, &QNetworkReply::finished, **** );

              Normally, you may do the following because it may finish immediately and the slot may not be called. Sigh!

                 if ( nullptr != m_reply ) { /* check reply is null or not first */
                     if ( m_reply->isRunning() ) { /* connect when it is still running */
                         connect( m_reply, &QNetworkReply::finished,
                                  this,    &getFinished() );
                     }
                     else { /* not running(already finished) and call slot directly while connection is not needed */
                         getFinished();
                     }
                 }
              
              H Offline
              H Offline
              hbatalha
              wrote on 21 Jul 2021, 21:43 last edited by
              #12

              @JoeCFD I didn't quite understand the code

              J 1 Reply Last reply 21 Jul 2021, 22:31
              0
              • H hbatalha
                21 Jul 2021, 21:43

                @JoeCFD I didn't quite understand the code

                J Offline
                J Offline
                JoeCFD
                wrote on 21 Jul 2021, 22:31 last edited by
                #13

                @hbatalha added comments.

                H 1 Reply Last reply 22 Jul 2021, 01:55
                0
                • F Offline
                  F Offline
                  fcarney
                  wrote on 21 Jul 2021, 22:41 last edited by fcarney
                  #14

                  We do something like this:

                  connect(reply, &QNetworkReply::finished, someobject, &SomeObject::someHandler); // or lambda
                  connect(reply, &QNetworkReply::finished, reply, &QObject::deleteLater);
                  

                  This keeps the lifetime of the reply separate from the "someobject".

                  C++ is a perfectly valid school of magic.

                  H 1 Reply Last reply 22 Jul 2021, 01:56
                  0
                  • H hbatalha
                    21 Jul 2021, 21:34

                    @SGaist said in Real confusion about when to delete QNetworkReply object:

                    You know that deleteLater does not change that pointer to a nullptr value ?

                    Yeah, which is making me confused on why it's crashing the app. Any ideas?

                    I made some changes in these two functions:

                    void FileDownloader::download(QString url, QString dest_file, QString downloadName, FileDownloadRequestType fileDownloadRequestType)
                    {
                        this->show();
                    
                        downloadBegan = false;
                        finished = false;
                    
                        connect(ui->pushButton, &QPushButton::clicked, [this] { this->close(); });
                    
                        if(MAIN.preferences.theme == Preferences::Theme::DARK)
                        {
                            ui->msg_label->setStyleSheet("color : white;");
                            ui->progress_label->setStyleSheet("color : white;");
                        }
                        else  if(MAIN.preferences.theme == Preferences::Theme::LIGHT)
                        {
                            ui->msg_label->setStyleSheet("color : black;");
                            ui->progress_label->setStyleSheet("color : black;");
                        }
                    
                        ui->download_progressBar->setMinimum(0);
                        ui->download_progressBar->setMaximum(0);
                    
                        ui->msg_label->setText(tr("Downloading ") + downloadName);
                    
                        reply = net.get(QNetworkRequest(QUrl(url)));
                    
                        downloadBegan = true;
                        finished = false;
                    
                        if(QFile::exists(dest_file))
                            QFile::remove(dest_file);
                    
                        QObject::connect(reply, &QNetworkReply::downloadProgress, [this](qint64 received, qint64 total)
                        {
                            qDebug() << received << " of " << total;
                    
                            ui->progress_label->setText(Util::displaySize(received) + " of " + Util::displaySize(total));
                    
                            ui->download_progressBar->setRange(0, total);
                            ui->download_progressBar->setValue(received);
                        });
                    
                        QObject::connect(&net, &QNetworkAccessManager::finished, [this, dest_file, fileDownloadRequestType](QNetworkReply* reply)
                        {
                            finished = true;
                    
                            if(! reply->error())
                            {
                                QByteArray data = reply->readAll();
                                reply->deleteLater();
                    
                                QFile file(dest_file);
                    
                                if( ! file.open(QIODevice::ReadWrite | QIODevice::Truncate))
                                    Util::displayErrorMessage(tr("Something went wrong"), tr("Could not save the file"), this, true);
                    
                                qDebug() << "Size written: " <<  file.write(data);
                                file.close();
                    
                                ui->msg_label->setText(tr("Download Complete"));
                                ui->progress_label->setText("");
                    
                                exe = dest_file;
                    
                                if(fileDownloadRequestType == FileDownloadRequestType::UPDATE)
                                    handleUpdateDownloadFinished();
                            }
                        });
                    }
                    
                    void FileDownloader::closeEvent(QCloseEvent *)
                    {
                        if( downloadBegan && ! finished)
                            reply->abort();
                    }
                    

                    Instead of using the reply object to check if the download has finished I am using bool variables. I call deleteLater in the slot.

                    J Offline
                    J Offline
                    JKSH
                    Moderators
                    wrote on 21 Jul 2021, 23:55 last edited by
                    #15

                    @hbatalha said in Real confusion about when to delete QNetworkReply object:

                    @SGaist said in Real confusion about when to delete QNetworkReply object:

                    You know that deleteLater does not change that pointer to a nullptr value ?

                    Yeah, which is making me confused on why it's crashing the app. Any ideas?

                    1. Most important: Set FileDownloader::reply to nullptr after you call deleteLater().
                    2. Second most important: Give your member variable a different name. Don't call it reply, since your lamba's parameter is also called reply. This causes shadowing (https://www.learncpp.com/cpp-tutorial/variable-shadowing-name-hiding/ )

                    Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                    1 Reply Last reply
                    3
                    • J Online
                      J Online
                      jeremy_k
                      wrote on 22 Jul 2021, 00:31 last edited by
                      #16

                      For Qt 5.14 and later, theres QNetworkAccessmanager::setAutoDeleteReplies() which calls deleteLater() after emitting the finished signal.

                      Asking a question about code? http://eel.is/iso-c++/testcase/

                      H 1 Reply Last reply 22 Jul 2021, 01:59
                      2
                      • J JoeCFD
                        21 Jul 2021, 22:31

                        @hbatalha added comments.

                        H Offline
                        H Offline
                        hbatalha
                        wrote on 22 Jul 2021, 01:55 last edited by
                        #17

                        @JoeCFD Thanks I understand now.
                        I didn't know that it could finish immediately and the slot may not be called. What would happen if that happens. I am curious.

                        /* check reply is null or not first */

                        Why would we do something like that. Is there any chance that the QNetworkAccessManager::get() may return a nullptr?

                        J 1 Reply Last reply 22 Jul 2021, 14:47
                        0
                        • F fcarney
                          21 Jul 2021, 22:41

                          We do something like this:

                          connect(reply, &QNetworkReply::finished, someobject, &SomeObject::someHandler); // or lambda
                          connect(reply, &QNetworkReply::finished, reply, &QObject::deleteLater);
                          

                          This keeps the lifetime of the reply separate from the "someobject".

                          H Offline
                          H Offline
                          hbatalha
                          wrote on 22 Jul 2021, 01:56 last edited by
                          #18

                          @fcarney said in Real confusion about when to delete QNetworkReply object:

                          connect(reply, &QNetworkReply::finished, reply, &QObject::deleteLater);

                          So this is the same as @jeremy_k answer: QNetworkAccessmanager::setAutoDeleteReplies() ?

                          1 Reply Last reply
                          0
                          • J jeremy_k
                            22 Jul 2021, 00:31

                            For Qt 5.14 and later, theres QNetworkAccessmanager::setAutoDeleteReplies() which calls deleteLater() after emitting the finished signal.

                            H Offline
                            H Offline
                            hbatalha
                            wrote on 22 Jul 2021, 01:59 last edited by
                            #19

                            @jeremy_k So that will eradicate the need to call deleteLater?

                            J 1 Reply Last reply 22 Jul 2021, 03:53
                            0
                            • H hbatalha
                              22 Jul 2021, 01:59

                              @jeremy_k So that will eradicate the need to call deleteLater?

                              J Offline
                              J Offline
                              JKSH
                              Moderators
                              wrote on 22 Jul 2021, 03:53 last edited by JKSH
                              #20

                              @hbatalha said in Real confusion about when to delete QNetworkReply object:

                              So that will eradicate the need to call deleteLater?

                              Yes, that means you don't need to call deleteLater() yourself. But you must still set your pointer to nullptr.

                              OR, avoid storing the QNetworkReply as a member variable.

                              Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                              H 1 Reply Last reply 22 Jul 2021, 14:34
                              0
                              • J JoeCFD
                                21 Jul 2021, 18:25

                                Use this to check if reply finishes before it is deleted.
                                connect( m_reply, &QNetworkReply::finished, **** );

                                Normally, you may do the following because it may finish immediately and the slot may not be called. Sigh!

                                   if ( nullptr != m_reply ) { /* check reply is null or not first */
                                       if ( m_reply->isRunning() ) { /* connect when it is still running */
                                           connect( m_reply, &QNetworkReply::finished,
                                                    this,    &getFinished() );
                                       }
                                       else { /* not running(already finished) and call slot directly while connection is not needed */
                                           getFinished();
                                       }
                                   }
                                
                                J Online
                                J Online
                                jeremy_k
                                wrote on 22 Jul 2021, 06:00 last edited by jeremy_k
                                #21

                                @JoeCFD said in Real confusion about when to delete QNetworkReply object:

                                Normally, you may do the following because it may finish immediately and the slot may not be called. Sigh!

                                Can you provide a citation? QNetworkAccessManager creates its own worker thread(s), and seems to use queued connections for communication with the worker objects that carry out the requests. I also don't see evidence of this extra check in the HTTP example

                                A demonstration that works for me (but proves nothing):

                                #include <QCoreApplication>
                                #include <QNetworkAccessManager>
                                #include <QNetworkReply>
                                #include <QUrl>
                                #include <QNetworkRequest>
                                #include <QDebug>
                                #include <QThread>
                                
                                int main(int argc, char *argv[])
                                {
                                    QCoreApplication a(argc, argv);
                                    QNetworkAccessManager manager;
                                    QUrl url("http://forum.qt.io");
                                    QNetworkRequest request(url);
                                    qDebug() << "Fetching" << url;
                                    QNetworkReply *reply = manager.get(request);
                                    QThread::sleep(60); // Wait a minute!
                                    QObject::connect(reply, &QNetworkReply::finished, []() {
                                        qDebug() << "request finished";
                                        QCoreApplication::instance()->quit();
                                    });
                                    return a.exec();
                                }
                                
                                

                                Asking a question about code? http://eel.is/iso-c++/testcase/

                                J 1 Reply Last reply 22 Jul 2021, 14:49
                                0
                                • J JKSH
                                  22 Jul 2021, 03:53

                                  @hbatalha said in Real confusion about when to delete QNetworkReply object:

                                  So that will eradicate the need to call deleteLater?

                                  Yes, that means you don't need to call deleteLater() yourself. But you must still set your pointer to nullptr.

                                  OR, avoid storing the QNetworkReply as a member variable.

                                  H Offline
                                  H Offline
                                  hbatalha
                                  wrote on 22 Jul 2021, 14:34 last edited by
                                  #22

                                  @JKSH

                                  Yes, that means you don't need to call deleteLater() yourself. But you must still set your pointer to nullptr.

                                  I only need to call it once in the constructor?

                                  OR, avoid storing the QNetworkReply as a member variable.

                                  The only reason I have it as a member variable is because I need a way to call abort() to cancel a download.

                                  J 1 Reply Last reply 23 Jul 2021, 01:39
                                  0
                                  • H hbatalha
                                    22 Jul 2021, 01:55

                                    @JoeCFD Thanks I understand now.
                                    I didn't know that it could finish immediately and the slot may not be called. What would happen if that happens. I am curious.

                                    /* check reply is null or not first */

                                    Why would we do something like that. Is there any chance that the QNetworkAccessManager::get() may return a nullptr?

                                    J Offline
                                    J Offline
                                    JoeCFD
                                    wrote on 22 Jul 2021, 14:47 last edited by JoeCFD
                                    #23

                                    @hbatalha said in Real confusion about when to delete QNetworkReply object:

                                    @JoeCFD Thanks I understand now.
                                    I didn't know that it could finish immediately and the slot may not be called. What would happen if that happens. I am curious.

                                    Read my code: if it finishes, you call the slot directly.

                                    /* check reply is null or not first */

                                    Why would we do something like that. Is there any chance that the QNetworkAccessManager::get() may return a nullptr?
                                    Pure habit.

                                    1 Reply Last reply
                                    0
                                    • J jeremy_k
                                      22 Jul 2021, 06:00

                                      @JoeCFD said in Real confusion about when to delete QNetworkReply object:

                                      Normally, you may do the following because it may finish immediately and the slot may not be called. Sigh!

                                      Can you provide a citation? QNetworkAccessManager creates its own worker thread(s), and seems to use queued connections for communication with the worker objects that carry out the requests. I also don't see evidence of this extra check in the HTTP example

                                      A demonstration that works for me (but proves nothing):

                                      #include <QCoreApplication>
                                      #include <QNetworkAccessManager>
                                      #include <QNetworkReply>
                                      #include <QUrl>
                                      #include <QNetworkRequest>
                                      #include <QDebug>
                                      #include <QThread>
                                      
                                      int main(int argc, char *argv[])
                                      {
                                          QCoreApplication a(argc, argv);
                                          QNetworkAccessManager manager;
                                          QUrl url("http://forum.qt.io");
                                          QNetworkRequest request(url);
                                          qDebug() << "Fetching" << url;
                                          QNetworkReply *reply = manager.get(request);
                                          QThread::sleep(60); // Wait a minute!
                                          QObject::connect(reply, &QNetworkReply::finished, []() {
                                              qDebug() << "request finished";
                                              QCoreApplication::instance()->quit();
                                          });
                                          return a.exec();
                                      }
                                      
                                      
                                      J Offline
                                      J Offline
                                      JoeCFD
                                      wrote on 22 Jul 2021, 14:49 last edited by JoeCFD
                                      #24

                                      @jeremy_k examples often show how things work. However, it may not include all you may need. I use it in posting and experienced issues.

                                      1 Reply Last reply
                                      0
                                      • H hbatalha
                                        22 Jul 2021, 14:34

                                        @JKSH

                                        Yes, that means you don't need to call deleteLater() yourself. But you must still set your pointer to nullptr.

                                        I only need to call it once in the constructor?

                                        OR, avoid storing the QNetworkReply as a member variable.

                                        The only reason I have it as a member variable is because I need a way to call abort() to cancel a download.

                                        J Offline
                                        J Offline
                                        JKSH
                                        Moderators
                                        wrote on 23 Jul 2021, 01:39 last edited by
                                        #25

                                        @hbatalha said in Real confusion about when to delete QNetworkReply object:

                                        @JKSH

                                        Yes, that means you don't need to call deleteLater() yourself. But you must still set your pointer to nullptr.

                                        I only need to call it once in the constructor?

                                        You must set your pointer to nullptr every time the object is deleted.

                                        Qt Doc Search for browsers: forum.qt.io/topic/35616/web-browser-extension-for-improved-doc-searches

                                        H 1 Reply Last reply 23 Jul 2021, 06:52
                                        0
                                        • J JKSH
                                          23 Jul 2021, 01:39

                                          @hbatalha said in Real confusion about when to delete QNetworkReply object:

                                          @JKSH

                                          Yes, that means you don't need to call deleteLater() yourself. But you must still set your pointer to nullptr.

                                          I only need to call it once in the constructor?

                                          You must set your pointer to nullptr every time the object is deleted.

                                          H Offline
                                          H Offline
                                          hbatalha
                                          wrote on 23 Jul 2021, 06:52 last edited by
                                          #26

                                          @JKSH This is how I did it

                                          QObject::connect(&net, &QNetworkAccessManager::finished, [this, dest_file, url, fileDownloadRequestType](QNetworkReply* reply)
                                              {
                                                  finished = true;
                                          
                                                  if(! reply->error())
                                                  {
                                                      QByteArray data = reply->readAll();
                                          
                                                      QFile file(dest_file);
                                          
                                                      if( ! file.open(QIODevice::ReadWrite | QIODevice::Truncate))
                                                          Util::displayErrorMessage(tr("Something went wrong"), tr("Could not save the file"), this, true);
                                          
                                                      file.write(data);
                                                      file.close();
                                          
                                                      ui->msg_label->setText(tr("Download Complete"));
                                                      ui->progress_label->setText("");
                                          
                                                      exe = dest_file;
                                          
                                                      if(fileDownloadRequestType == FileDownloadRequestType::UPDATE)
                                                          handleUpdateDownloadFinished();
                                                  }
                                                  else if(reply->errorString() != "Operation canceled")
                                                  {
                                                      LOG_ERROR("Error Downloading from " + url + "\nDescription: " +  reply->errorString());
                                                      reply = nullptr;
                                                      Util::displayErrorMessage(tr("Network Error"), tr("Please try again later"), this, true);
                                                  }
                                          
                                                  m_reply = nullptr;
                                              });
                                          
                                          jsulmJ 1 Reply Last reply 23 Jul 2021, 06:56
                                          0

                                          16/31

                                          22 Jul 2021, 00:31

                                          • Login

                                          • Login or register to search.
                                          16 out of 31
                                          • First post
                                            16/31
                                            Last post
                                          0
                                          • Categories
                                          • Recent
                                          • Tags
                                          • Popular
                                          • Users
                                          • Groups
                                          • Search
                                          • Get Qt Extensions
                                          • Unsolved