【问题标题】:What happens to the signal/slot connection if the pointer is pointing to a new object如果指针指向一个新对象,信号/槽连接会发生什么
【发布时间】:2012-03-20 01:24:53
【问题描述】:

我有一个带有以下代码的上传器:

if(!_canceled) {
    _reply = _accessManager.put(request, item);

    if(_reply) { 
        _currentItem = item;

        bool status = connect(_reply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(reportUploadProgress(qint64, qint64)));
        pantheios::log(pantheios::debug, "StorageProvider(): connection uploadProgress(qint64, qint64), status", pantheios::boolean(status));
        status = connect(_reply, SIGNAL(finished()), this, SLOT(handleFinishedRequest()));
        pantheios::log(pantheios::debug, "StorageProvider(): connection finished(), status", pantheios::boolean(status));

    } else {
        emit noReply();
        pantheios::log(pantheios::error, "StorageProvider(): no reply", item.toUtf8());
    }

然后在完成的插槽中我这样做:

QNetworkReply *reply =  qobject_cast<QNetworkReply*>(sender());

if(reply->error() > QNetworkReply::NoError) { 
    pantheios::log(pantheios::error, "StorageProvider(handleFinishedRequest) ", reply->errorString().toUtf8());

    if((reply->error() == QNetworkReply::TemporaryNetworkFailureError) || (reply->error() == QNetworkReply::ContentReSendError)) {
                    // retry the last request
        _reply = accessManager.put(reply->request(), _currentItem);
    }

} else {
    ...
}

此 StorageProvider 将能够处理不同的请求,并且响应将具有不同的连接,具体取决于它创建的函数。回复是成员变量的原因是我可以在下一个请求之前将其删除。

所以,我的问题是,如果我重新回复回复,我是否必须再次进行连接?插槽/信号是否连接到指针或对象?另外,有没有更好的方法来删除旧回复?

编辑:将代码更改为已完成请求的处理程序;

if(_currentReply->error() > QNetworkReply::NoError) { 
    pantheios::log(pantheios::error, "StorageProvider(handleFinishedRequest) ", _currentReply->errorString().toUtf8());

    if(((_currentReply->error() == QNetworkReply::TemporaryNetworkFailureError) || (_currentReply->error() == QNetworkReply::ContentReSendError)) && (_currentRetries < 4)) {

        QNetworkRequest lastRequest = _currentReply->request();
        _currentReply->deleteLater();

        _currentReply = _accessManager.put(lastRequest, _currentItem);

        if(_currentReply) { 

            bool status = connect(_currentReply, SIGNAL(uploadProgress(qint64, qint64)), this, SLOT(reportUploadProgress(qint64, qint64)));
            pantheios::log(pantheios::debug, "StorageProvider(retry): connection uploadProgress(qint64, qint64), status", pantheios::boolean(status));
            status = connect(_currentReply, SIGNAL(finished()), this, SLOT(handleFinishedRequest()));
            pantheios::log(pantheios::debug, "StorageProvider(retry): connection finished(), status", pantheios::boolean(status));

        } else {
            emit noReply();
            pantheios::log(pantheios::error, "StorageProvider(retry): AccessManager no reply");
        }
    }

}

【问题讨论】:

    标签: c++ qt signals-slots


    【解决方案1】:

    信号连接的是对象,而不是指针变量,所以每次都要建立新的连接。

    要删除回复,只需在finished() 槽的开头调用deleteLater()

    QNetworkReply *reply =  qobject_cast<QNetworkReply*>(sender());
    reply->deleteLater();
    

    这样它将在插槽执行结束时被删除。


    您可以通过将每个请求(初始请求加上所有重试)包装在其自己的 QObject 派生类中来避免使用 sender()
    class PutRequest : public QObject {
    Q_OBJECT
    private:
         QNetworkAccessManager *_manager;
         QNetworkReply *_reply;
         QIODevice *_item; // Or whatever type the second parameter of put is
    public:
         explicit PutRequest(QNetworkAccessManager *manager, QNetworkRequest *request, QIODevice *item, QObject *parent = 0)
           : QObject(parent), _manager, _reply(0), _item(item) 
         {            
             _reply = _manager.put(request, item);
             connectSignalsAndSlots();
         }       
    
    private:
         void connectSignalsAndSlots() {
             // to delete the reply if the PutRequest object is destroyed
             reply_->setParent(this);
             // since the reply is encapsulated, the object has to emit its own
             // upload progress signal
             connect(_reply, SIGNAL(uploadProgress(qint64, qint64)), this, SIGNAL(uploadProgress(qint64, qint64)));
             connect(_reply, SIGNAL(finished()), this, SLOT(handleFinishedRequest()));
         }
    
    private slots:
         void handleFinishedRequest() {
             _reply->deleteLater();
             if(reply->error() != QNetworkReply::NoError) {
                   pantheios::log(pantheios::error, "StorageProvider(handleFinishedRequest) ", reply->errorString().toUtf8());
    
             if((_reply->error() == QNetworkReply::TemporaryNetworkFailureError) || (reply->error() == QNetworkReply::ContentReSendError)) {
                // retry the last request
                _reply = _manager.put(_reply->request(), _item);
                connectSignalsAndSlots();
             }
    
             } else {
             ...                 
                 emit finished(this);
             }         
         }
    signals:
         void uploadProgress(qint64, qint64);
         // emitted when the upload is successful (after the possible retries)
         void finished(PutRequest*);
    };
    

    你会像这样创建请求:

    if(!_canceled) {
        PutRequest *putRequest = new PutRequest(_accessManager, request, item);
    
        // and you connect putRequest object signals to whatever you connected _reply to
    

    【讨论】:

    • 在尝试执行重试逻辑时我还应该记住什么?在qt方面有没有好的方法练习?跟踪什么上传我正在重新使用等的方法。atm 我当时只有一个请求,然后保存上传项目以便在重新使用时能够使用它。
    • 如果我使用回复的请求然后删除回复会怎样?
    • 您的代码唯一的另一个问题是您通常应该避免使用sender()(Qt 文档解释了原因)。您可以创建一个QObject 派生类来处理每次下载,其中包含一个_reply 成员、初始请求和它自己的finished() 插槽,并且您可以使用相同的“下载”对象进行重试。
    • QNetworkRequest 未链接到 QNetworkReply 对象,因此您可以安全地复制它并在之后删除回复。
    • 所以如果类有一个“currentItem”等是当前正在上传的对象,它就不会工作?该类将只处理一个呼叫,并且在该呼叫完成之前永远不会启动一个新呼叫。这是一条路吗?
    猜你喜欢
    • 2022-12-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-02-09
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多