一、坑的现象
QObject对象释放不当,会导致崩溃或者内存泄露
二、遇坑的原因
1、 如果事件队列中, 某QObject对象有事件等待未处理,直接delete后,再接着执行它的槽函数会导致崩溃
例子:
//创建一个QObject对象,并且有一个槽函数
class A : public QObject
{
Q_OBJECT
public:
explicit A(QObject *parent = nullptr);
~A()
{
qDebug() << "~A";
}
public slots:
void sltTest();
};
//qt的线程对象
MyThread::MyThread(QObject *parent) : QObject(parent)
{
QThread* pThread = new QThread();
this->moveToThread(pThread);
pThread->start();
}
void MyThread::setObject(QObject *object)
{
m_mainObject = object;
}
void MyThread::sltRun()
{
int n = 0;
delete m_mainObject;
}
//main中创建对象A,创建子线程MyThread,并在子线程中运行sltRun函数,在子线程中删除对象
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MainWindow w;
w.show();
MyThread *my = new MyThread;
A *testMainObject = new A;
my->setObject(testMainObject);
QTimer::singleShot(1000, [=](){
QMetaObject::invokeMethod(my, "sltRun");
});
//对象删除后,再往事件队列中扔一个testMainObject的槽函数,当执行这个槽函数的时候,testMainObject已经变成野指针,导致程序崩溃
QTimer::singleShot(1000, [=](){
QMetaObject::invokeMethod(testMainObject, "sltTest");
});
qDebug() << "run~~~";
return a.exec();
}
上述代码执行后,大概率会发生崩溃现象,原因是QObject对象释放后,再继续使用,这时qt文档建议,释放QObject对象使用deleteLater()函数,deleteLater保证事件队列中该对象的槽函数执行完成后 ,再执行释放操作
2、 在QThread::run线程中,就算调用了processEvents,调用QObject对象的deleteLater函数,deleteLater也不会执行,会造成内存泄露
在上述代码的基础上在sltRun函数中添加如下代码
void MyThread::sltRun()
{
while (1)
{
QThread::sleep(2);
qDebug() << 123;
A *a = new A;
QMetaObject::invokeMethod(a, "sltTest");
QMetaObject::invokeMethod(a, "deleteLater");
a->deleteLater();
qApp->processEvents(QEventLoop::AllEvents, 10);
}
}
只有sltTest中的日志会打印,A对象的析构函数日志没有打印,qt官方文档是这样解释:
Note that entering and leaving a new event loop (e.g., by opening a modal dialog) will not perform the deferred deletion; for the object to be deleted, the control must return to the event loop from which deleteLater() was called
得出结论,不管QThread是用run方式还是moveToThread方式起来的,如果一直在一个函数中做循环操作如while(1),就算在循环中调用processEvents,属于该线程的普通槽函数会执行,但deleteLater不会执行,deleteLater只有在控制权限回到事件队列中,才会执行,这样就导致如果线程的run函数或者sltRun中一直不终端,并且不停地调用deleteLater,就会有内存泄露发生,程序内存会不断的增长,且很难发现