一、问题描述
QNetworkAccessManager无疑是C++中比较强大的网络库,其中就能够利用它实现http网络请求的功能,但是在我的某个应用场景中可能会有以下问题:
//封装好的网络接口
void HttpRequest::postDataToServerAsync(const QString &httpPath, const QByteArray &postBody,std::function<void (const HttpResultPackge &)> &callBack)
{
//...
QNetworkAccessManager *netAm = new QNetworkAccessManager();
connect(netAm, &QNetworkAccessManager::finished, this, [callBack](QNetworkReply *amReply){
HttpResultPackge result;
//...
callBack(result);
});
auto reply = netAm->post(request, postBody);
//...
}
上述代码为一种常见的异步网络接口实现方法,在http数据返回后通过callBack返回到调用者去执行后续代码
void MainWindow::httpPost()
{
HttpRequest::getInstance().postDataToServerAsync(httpPath, postBody, [this](const HttpResultPackge &data){
//...
});
}
但是这种方法有一种情况会使程序出现异常:当MainWindow调用请求后又进行析构,之后网络请求数据到达后通过callBack进入lambda函数中,假如该函数有使用MainWindow中的成员变量就会发送异常,这种情况在网络状态不好的时候经常发生
提示:以下是本篇文章正文内容,下面案例可供参考
二、解决办法
1.使用QPointer
利用QT中的QPointer特性,构造一个指向MainWindow的QPointer,将QPointer捕捉到lambda函数中,之后判断一下MainWindow是否为空
void MainWindow::httpPost()
{
QPointer<MainWindow> p(this);
HttpRequest::getInstance().postDataToServerAsync(httpPath, postBody, [this, p](const HttpResultPackge &data){
if(p.isNull())
return;
//...使用了this中的变量
});
}
注意:此方法仅限于继承QObject的对象
2.利用信号与槽
参考qobject.cpp中的QObject::~QObject()函数,当对象析构后,会将所有相关的信号和槽断开连接
因此只需要编写一个信号转发类,然后用MainWindow去接受信号,当MainWindow被析构时,连接自动断开,也就不会处理槽函数的内容了
class HttpFinishedSender : public QObject
{
Q_OBJECT
signals:
void httpFinished(const HttpResultPackge &data);
}
//......
void MainWindow::httpPost()
{
auto httpSender = new HttpFinishedSender(this);
connect(httpSender, &HttpFinishedSender::httpFinished, this, [this](const HttpResultPackge &data){
//...使用了this中的变量
});
HttpRequest::getInstance().postDataToServerAsync(httpPath, postBody, [httpSender](const HttpResultPackge &data){
emit httpSender->httpFinished(data);
delete httpSender;
});
}
注意:此方法仅限于继承QObject的对象
4.记录网络请求量
在成员变量中使用一个变量专门管理当前对象正在进行的网络请求数量,之后在析构中阻塞判断,直到所有请求完毕
MainWindow::~MainWindow()
{
//TODO 中断网络请求
while(m_httpNum > 0){
//主线程可添加事件循环避免卡界面
QEventLoop loop;
loop.processEvents(QEventLoop::AllEvents, 200);
}
}
此方法需要Http请求类去手动实现中断机制,不然在弱网环境下会循环很久,对于前端来说还好用户感知不到,但是对后端来说会消耗很高的性能不推荐使用
3.静态变量(不推荐)
此方法只适用于同时存在一个对象,繁杂还不标准,并且可能有未知的不确定性,如果调用者确实不能继承QObject可考虑使用
static bool isDelete = false;
//在构造和析构时都需要处理静态变量
MainWindow::MainWindow()
{
isDelete = false;
}
MainWindow::~MainWindow()
{
isDelete = true;
}
void MainWindow::httpPost()
{
HttpRequest::getInstance().postDataToServerAsync(httpPath, postBody, [this, p](const HttpResultPackge &data){
if(isDelete)
return;
//...使用了this中的变量
});
}