元对象编译器moc(Meta-Object Compiler)是Qt对C++的扩展。可以先把它看作一个代码生成器。以test类为例,构建项目时,moc读取C++头文件。如果在test.h中找到Q_OBJECT宏,它将生成一个moc_test.cpp,其中包含test类的元对象(metaObject)代码。这个新的文件和test.cpp一起参与编译,最终被链接到二进制代码中去。
Qt 将源代码交给标准 C++编译器,如gcc之前,需要事先将扩展的语法(Q_OBJECT,SLOT,SIGNAL等)展开来。完成这一操作的就是moc。
Qt 不是使用的“标准的” C++ 语言,而是对其进行了一定程度的“扩展”。这里我们从Qt新增加的关键字就可以看出来:signals、slots 或者 emit。所以有人会觉得 Qt 的程序编译速度慢,这主要是因为在 Qt 将源代码交给标准 C++ 编译器,如 gcc 之前,需要事先将这些扩展的语法去除掉。完成这一操作的就是 moc。
moc 全称是 Meta-Object Compiler,也就是“元对象编译器”(位置:C:\Qt\5.9.8\msvc2015\bin\moc.exe)。作用:程序编译时调用mac对程序源码解析。
Qt 程序在交由标准编译器编译之前,先要使用 moc 分析 C++ 源文件。如果它发现在一个头文件中包含了宏 Q_OBJECT,则会生成另外一个 C++ 源文件。这个源文件中包含了 Q_OBJECT 宏的实现代码。这个新的文件名字将会是原文件名前面加上 moc_ 构成。这个新的文件同样将进入编译系统,最终被链接到二进制代码中去。因此我们可以知道,这个新的文件不是“替换”掉旧的文件,而是与原文件一起参与编译。另外,我们还可以看出一点,moc 的执行是在预处理器之前。因为预处理器执行之后,Q_OBJECT 宏就不存在了。
#ifndef WIDGET_H
#define WIDGET_H
#include
QT_BEGIN_NAMESPACE
namespace Ui { class Widget; }
QT_END_NAMESPACE
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget *parent = nullptr);
~Widget();
signals:
void signal_test1();
void signal_test2(int index);
public slots:
void slot_test1(){};
void slot_test2(int index){};
private:
Ui::Widget *ui;
};
#endif // WIDGET_H
编译时生成的moc_test.cpp
/****************************************************************************
** Meta object code from reading C++ file 'widget.h'
**
** Created by: The Qt Meta Object Compiler version 67 (Qt 5.9.8)
**
** WARNING! All changes made in this file will be lost!
*****************************************************************************/
#include "../../testwidget/widget.h"
#include
#include
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'widget.h' doesn't include ."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.9.8. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif
QT_BEGIN_MOC_NAMESPACE
QT_WARNING_PUSH
QT_WARNING_DISABLE_DEPRECATED
struct qt_meta_stringdata_Widget_t {
QByteArrayData data[7];
char stringdata0[62];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
qptrdiff(offsetof(qt_meta_stringdata_Widget_t, stringdata0) + ofs \
- idx * sizeof(QByteArrayData)) \
)
static const qt_meta_stringdata_Widget_t qt_meta_stringdata_Widget = {
{
QT_MOC_LITERAL(0, 0, 6), // "Widget"
QT_MOC_LITERAL(1, 7, 12), // "signal_test1"
QT_MOC_LITERAL(2, 20, 0), // ""
QT_MOC_LITERAL(3, 21, 12), // "signal_test2"
QT_MOC_LITERAL(4, 34, 5), // "index"
QT_MOC_LITERAL(5, 40, 10), // "slot_test1"
QT_MOC_LITERAL(6, 51, 10) // "slot_test2"
},
"Widget\0signal_test1\0\0signal_test2\0"
"index\0slot_test1\0slot_test2"
};
#undef QT_MOC_LITERAL
static const uint qt_meta_data_Widget[] = {
// content:
7, // revision
0, // classname
0, 0, // classinfo
4, 14, // methods
0, 0, // properties
0, 0, // enums/sets
0, 0, // constructors
0, // flags
2, // signalCount
// signals: name, argc, parameters, tag, flags
1, 0, 34, 2, 0x06 /* Public */,
3, 1, 35, 2, 0x06 /* Public */,
// slots: name, argc, parameters, tag, flags
5, 0, 38, 2, 0x0a /* Public */,
6, 1, 39, 2, 0x0a /* Public */,
// signals: parameters
QMetaType::Void,
QMetaType::Void, QMetaType::Int, 4,
// slots: parameters
QMetaType::Void,
QMetaType::Void, QMetaType::Int, 4,
0 // eod
};
void Widget::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
Widget *_t = static_cast(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->signal_test1(); break;
case 1: _t->signal_test2((*reinterpret_cast< int(*)>(_a[1]))); break;
case 2: _t->slot_test1(); break;
case 3: _t->slot_test2((*reinterpret_cast< int(*)>(_a[1]))); break;
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast(_a[0]);
{
typedef void (Widget::*_t)();
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Widget::signal_test1)) {
*result = 0;
return;
}
}
{
typedef void (Widget::*_t)(int );
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&Widget::signal_test2)) {
*result = 1;
return;
}
}
}
}
const QMetaObject Widget::staticMetaObject = {
{ &QWidget::staticMetaObject, qt_meta_stringdata_Widget.data,
qt_meta_data_Widget, qt_static_metacall, nullptr, nullptr}
};
const QMetaObject *Widget::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *Widget::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_Widget.stringdata0))
return static_cast(this);
return QWidget::qt_metacast(_clname);
}
int Widget::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QWidget::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < 4)
qt_static_metacall(this, _c, _id, _a);
_id -= 4;
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id < 4)
*reinterpret_cast(_a[0]) = -1;
_id -= 4;
}
return _id;
}
// SIGNAL 0
void Widget::signal_test1()
{
QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}
// SIGNAL 1
void Widget::signal_test2(int _t1)
{
void *_a[] = { nullptr, const_cast(reinterpret_cast(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 1, _a);
}
QT_WARNING_POP
QT_END_MOC_NAMESPACE
定义在qobjectdefs.h
/* qmake ignore Q_OBJECT */
#define Q_OBJECT \
public: \
QT_WARNING_PUSH \
Q_OBJECT_NO_OVERRIDE_WARNING \
static const QMetaObject staticMetaObject; \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
QT_TR_FUNCTIONS \
private: \
Q_OBJECT_NO_ATTRIBUTES_WARNING \
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
QT_WARNING_POP \
struct QPrivateSignal {}; \
QT_ANNOTATE_CLASS(qt_qobject, "")
宏会在预编译期被具体的字符串所代替,那么我们在头文件中用到的Q_OBJECT宏就会被展开为上面的代码。因此需要实现Q_OBJECT扩展后所带来的变量和函数的定义。而这些定义都已经被写入到了moc_test.cpp文件中,这也就是为什么在Makefile中需要将moc_test.cpp一起编译的原因了。否则,这个类是不完整的,那肯定也是不可能编译通过的.
每一个定义了Q_OBJECT宏的,直接或者间接继承于QObject的类 有包含一个名为staticMetaObject的静态QMetaObject对象, 一个私有的静态函数qt_static_metacall。
它们为该类型的所有对象共有,同时它也继承了每一个祖父对象这两个静态成员和函数。
virtual const QMetaObject *metaObject() const; \ 用于获取类静态拥有的元对象
virtual void *qt_metacast(const char *); \ 通过元对象获取对象指针
virtual int qt_metacall(QMetaObject::Call, int, void **); \ 用于信号槽机制
每个不同的类 在MOC生成代码的时候会重写写这些虚函数。
QMetaObject提供的信息:
下面通过QMetaObject的接口来解读QMetaObject提供的信息:
1、基本信息
const char * className () const;
const QMetaObject * superClass () const
2、classinfo: 提供额外的类信息。其实就是一些名值对。 用户可以在类的声明中以Q_CLASSINFO(name, value)方式添加。
int classInfoCount () const
int classInfoOffset () const
QMetaClassInfo classInfo ( int index ) const
int indexOfClassInfo ( const char * name ) const
3、contructor:提供该类的构造方法信息
QMetaMethod constructor ( int index ) const
int constructorCount () const
int indexOfConstructor ( const char * constructor ) const
4、enum:描述该类声明体中所包含的枚举类型信息
QMetaEnum enumerator ( int index ) const
int enumeratorCount () const
int enumeratorOffset () const
int indexOfEnumerator ( const char * name ) const
5、method:描述类中所包含方法信息:包括property,signal,slot等,包括祖先类,如何组织暂时不确定。
QMetaMethod method ( int index ) const
int methodCount () const
int methodOffset () const
int indexOfMethod ( const char * method ) const
int indexOfSignal ( const char * signal ) const
int indexOfSlot ( const char * slot ) const
6、property:类型的属性信息
QMetaProperty property ( int index ) const
int propertyCount () const
int propertyOffset () const
int indexOfProperty ( const char * name ) const
QMetaProperty userProperty () const //返回类中设置了USER flag的属性,(难道只能有一个这样的属性?)
注意:对于类里面定义的函数,构造函数,枚举,只有加上一些宏才表示你希望为方法提供meta信息。比如 Q_ENUMS用来注册宏,
Q_INVACABLE用来注册方法(包括构造函数)。Qt这么设计的原因应该是避免meta信息的臃肿。
在QMetaObject类中有个重要的结构体d:元对象 的所有数据都由该结构定义,
struct { // private data
const QMetaObject *superdata;//父类的staticMetaObject指针
const QByteArrayData *stringdata;// 字符串数组,保存类的 类名,槽函数名 信号函数名等 字符串信息
const uint *data; //无符号整形数组,该数组是个预定义的复合数据结构,由QMetaObjectPrivate 类提供管理,保存了类的基本信息,和一些索引值
typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, int, void **);
StaticMetacallFunction static_metacall;
const QMetaObject * const *relatedMetaObjects;
void *extradata; //reserved for future use
} d;
分析moc_test.cpp中的内容:
QMetaObject包含了QObject的所谓的元数据,也就是QObject信息的一些描述信息:除了类型信息外,还包含QT中特 有的signal&slot信息。
QObject::metaObject ()方法返回一个QObject对象对应的metaobject对象,注意这个方法是virtual方法。如果一个类的声明中包含了 Q_OBJECT宏,编译器会生成代码来实现这个类对应的QMetaObject类,并重载QObject::metaObject()方法来返回这个 QMetaObject类的实例引用。这样当通过QObject类型的引用调用metaObejct方法时,返回的是这个引用的所指的真实对象的 metaobject。
如果一个类从QObject派生,确没有声明Q_OBJECT宏,那么这个类的metaobject对象不会被生成,这样这个类所声明的 signal slot都不能使用,而这个类实例调用metaObject()返回的就是其父类的metaobject对象,这样导致的后果就是你从这个类实例获得的元 数据其实都是父类的数据,这显然给你的代码埋下隐患。因此如果一个类从QOBject派生,它都应该声明Q_OBJECT宏,不管这个类有没有定义 signal&slot和Property。
//初始化静态变量staticMetaObject
const QMetaObject test::staticMetaObject = {
{ &QWidget::staticMetaObject,// //基类元对象
qt_meta_stringdata_test.data,
qt_meta_data_test, //元数据表
qt_static_metacall, //用于执行对象所对应的信号或槽,或查找槽索引
nullptr, nullptr}
};
在QMetaObject结构体的定义中,只有一个成员变量d,上面的赋值也是对d的赋值构造
每一项的含义:
const QMetaObject *superdata;//父类的staticMetaObject指针
const QByteArrayData *stringdata;// 字符串数组,保存类的 类名,槽函数名 信号函数名等 字符串信息
const uint *data; //无符号整形数组,该数组是个预定义的复合数据结构,由QMetaObjectPrivate 类提供管理,保存了类的基本信息,和一些索引值
从 moc_widget.cpp中看到staticMetaObject的构造:
第一项是&QWidget::staticMetaObject;毫无疑问,Widget继承于QWidget,因此是父类QWidget的staticMetaObject指针
第二项:
首先看1 qt_meta_stringdata_Widget_t ,由两部分组成:QByteArrayData和stringdata0
QT_MOC_LITERAL宏的作用是为stringdata0中保存的每个函数名都创建一个QByteArrayData,宏参数为函数的索引值,偏移量,函数名长度。举例:
它是怎么使用的呢:
src\corelib\kernel\qmetaobject.cpp 对qmetaobject的方法提供了实现:
如
/*!
Returns the number of methods in this class, including the number of
methods provided by each base class. These include signals and slots
as well as normal member functions.
Use code like the following to obtain a QStringList containing the methods
specific to a given class:
\snippet code/src_corelib_kernel_qmetaobject.cpp methodCount
\sa method(), methodOffset(), indexOfMethod()
*/
int QMetaObject::methodCount() const
{
int n = priv(d.data)->methodCount;
const QMetaObject *m = d.superdata;
while (m) {
n += priv(m->d.data)->methodCount;
m = m->d.superdata;
}
return n;
}
返回类的方法的个数,首先调用了一个priv函数,参数即是:d.data
priv:定义在src\corelib\kernel\qmetaobject.cpp
static inline const QMetaObjectPrivate *priv(const uint* data)
{ return reinterpret_cast(data); }
返回的是QMetaObjectPrivate类型:src\corelib\kernel\qmetaobject_p.h
QMetaObjectPrivate 对类的版本,类名,类信息,方法,属性等做了描述
其中1 :代表 该信号在d.stringdata中的下标为1 因此可以从d.stringdata中找到该信号的名称
0: 代表信号的参数个数void signal_stest1() 中参数为0,
34 :代表信号的参数内容在该数组qt_meta_data_Widget中存放在下标[34]中,即
2:表示包含返回值一共两个参数
0x06:表示public
从上面的代码中,我们得知Qt的元对象系统:信号槽,属性系统,运行时类信息都存储在静态对象staticMetaObject中,其中stringdata和data中包含了绝大多数信息。为了性能,它们用数组下标访问,生成的代码也是下标索引代替,对空间占用极度节省。
返回当前的QMetaObject
const QMetaObject *test::metaObject() const
{
return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
类型转换
void *test::qt_metacast(const char *_clname)
{
if (!_clname) return nullptr;
if (!strcmp(_clname, qt_meta_stringdata_test.stringdata0))
return static_cast(this);
return QWidget::qt_metacast(_clname);
}
在类中只用定义信号,而不用实现信号,是因为在moc_test.cpp已经实现好了。
// SIGNAL 0
void test::signal_test1()
{
QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}
// SIGNAL 1
void test::signal_test2(int _t1)
{
void *_a[] = { nullptr, const_cast(reinterpret_cast(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 1, _a);
}
它们唯一的区别就是调用 QMetaObject::activate 函数时给出的参数不同,一个是0,一个是1,它们的含义是什么呢?它们表示是这个类中的第几个signal被发送出来了,回头再去看头文件就会发现它们就 是在这个类定义中,signal定义出现的顺序,这个参数可是非常重要的,它直接决定了进入这个函数体之后所发生的事情。
当执行流程进入到QMetaObject::activate函数中后,会先从connectionLists这个变量中取出与这个signal相对应的 connection list,它根据的就是刚才所传入进来的signal index。这个connection list中保存了所有和这个signal相链接的slot的信息,每一对connection(即:signal 和 slot 的连接)是这个list中的一项。
在每个一具体的链接记录中,还保存了这个链接的类型,是自动链接类型,还是队列链接类型,或者是阻塞链接类型,不同的类型处理方法还不一样的。这里,我们就只说一下直接调用的类型。
对于直接链接的类型,先找到接收这个signal的对象的指针,然后是处理这个signal的slot的index,已经是否有需要处理的参数,然后就使用这些信息去调用receiver的qt_metcall 方法。
在qt_metcall方法中就简单了,根据slot的index,一个大switch语句,调用相应的slot函数就OK了。
实际上调用qt_static_metacall
int test::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = QWidget::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < 4)
qt_static_metacall(this, _c, _id, _a);//调用qt_static_metacall
_id -= 4;
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id < 4)
*reinterpret_cast(_a[0]) = -1;
_id -= 4;
}
return _id;
}
根据函数索引调用槽函数,在这里可以看出信号函数也可以当作槽函数一样被调用(这也是为什么信号可以connect的槽函数位置),这也是信号槽调用过程的最后一步
void test::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
test *_t = static_cast(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->signal_test1(); break;
case 1: _t->signal_test2((*reinterpret_cast< int(*)>(_a[1]))); break;
case 2: _t->slot_test1(); break;
case 3: _t->slot_test2((*reinterpret_cast< int(*)>(_a[1]))); break;
default: ;
}
} else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast(_a[0]);
{
typedef void (test::*_t)();
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&test::signal_test1)) {
*result = 0;
return;
}
}
{
typedef void (test::*_t)(int );
if (*reinterpret_cast<_t *>(_a[1]) == static_cast<_t>(&test::signal_test2)) {
*result = 1;
return;
}
}
}
}
signals
# define QT_ANNOTATE_ACCESS_SPECIFIER(x)
# define Q_SIGNALS public QT_ANNOTATE_ACCESS_SPECIFIER(qt_signal)
# define signals Q_SIGNALS
看到了吗,如果signals被展开的话就是public,所以所有的信号都是公有的,也不需要像槽一样加public,protected,private的限定符。
slots
# define QT_ANNOTATE_ACCESS_SPECIFIER(x)
# define Q_SLOTS QT_ANNOTATE_ACCESS_SPECIFIER(qt_slot)
# define slots Q_SLOTS
slots和signals一样,只是没有了限定符,需要加public,protected,or privated
emit
它的宏定义:# define emit
emit后面也没有字符串!当它被替换的时候,程序其实就是调用了信号函数,而不是真正意义上的发送一个信号,有很多初学者都是认为当emit的时候,Qt会发信号,所以才会有很多人问“当emit之后,会不会立即执行其后面的代码”。当然,如果想让emit后面的代码不需要等槽函数执行完就开始执行的话,可以设置connect第5个参数。
moc查找头文件中的signals,slots,标记出信号和槽。将信号槽信息存储到类静态变量staticMetaObject中,并且按声明顺序进行存放,建立索引。当发现有connect连接时,将信号槽的索引信息放到一个双向链表中,彼此配对。当调用emit时,调用信号函数,并且传递发送信号的对象指针,元对象指针,信号索引,参数列表到active函数。通过active函数找到在双向链表中找到所有与信号对应的槽索引根据槽索引找到槽函数,执行槽函数。以上,便是信号槽的整个流程,总的来说就是一个“注册-索引”机制,并不存在发送系统信号之类的事情。
注意,都是以connect第五个参数是默认为前提的。
//Connect a signal to a pointer to qobject member function
template
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal,
const typename QtPrivate::FunctionPointer::Object *receiver, Func2 slot,
Qt::ConnectionType type = Qt::AutoConnection)
{
typedef QtPrivate::FunctionPointer SignalType;
typedef QtPrivate::FunctionPointer SlotType;
Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro::Value,
"No Q_OBJECT in the class with the signal");
//compilation error if the arguments does not match.
Q_STATIC_ASSERT_X(int(SignalType::ArgumentCount) >= int(SlotType::ArgumentCount),
"The slot requires more arguments than the signal provides.");
Q_STATIC_ASSERT_X((QtPrivate::CheckCompatibleArguments::value),
"Signal and slot arguments are not compatible.");
Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible::value),
"Return type of the slot is not compatible with the return type of the signal.");
const int *types = Q_NULLPTR;
if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
types = QtPrivate::ConnectionTypes::types();
return connectImpl(sender, reinterpret_cast(&signal),
receiver, reinterpret_cast(&slot),
new QtPrivate::QSlotObject::Value,
typename SignalType::ReturnType>(slot),
type, types, &SignalType::Object::staticMetaObject);
}
/*!
\internal
Implementation of the template version of connect
\a sender is the sender object
\a signal is a pointer to a pointer to a member signal of the sender
\a receiver is the receiver object, may not be null, will be equal to sender when
connecting to a static function or a functor
\a slot a pointer only used when using Qt::UniqueConnection
\a type the Qt::ConnctionType passed as argument to connect
\a types an array of integer with the metatype id of the parameter of the signal
to be used with queued connection
must stay valid at least for the whole time of the connection, this function
do not take ownership. typically static data.
If null, then the types will be computed when the signal is emit in a queued
connection from the types from the signature.
\a senderMetaObject is the metaobject used to lookup the signal, the signal must be in
this metaobject
*/
QMetaObject::Connection QObject::connectImpl(const QObject *sender, void **signal,
const QObject *receiver, void **slot,
QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
const int *types, const QMetaObject *senderMetaObject)
{
if (!signal) {
qWarning("QObject::connect: invalid null parameter");
if (slotObj)
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
int signal_index = -1;
void *args[] = { &signal_index, signal };
for (; senderMetaObject && signal_index < 0; senderMetaObject = senderMetaObject->superClass()) {
senderMetaObject->static_metacall(QMetaObject::IndexOfMethod, 0, args);
if (signal_index >= 0 && signal_index < QMetaObjectPrivate::get(senderMetaObject)->signalCount)
break;
}
if (!senderMetaObject) {
qWarning("QObject::connect: signal not found in %s", sender->metaObject()->className());
slotObj->destroyIfLastRef();
return QMetaObject::Connection(0);
}
signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject);
return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject);
}
/*!
\internal
Internal version of connect used by the template version of QObject::connect (called via connectImpl) and
also used by the QObjectPrivate::connect version used by QML. The signal_index is expected to be relative
to the number of signals.
*/
QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int signal_index,
const QObject *receiver, void **slot,
QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
const int *types, const QMetaObject *senderMetaObject)
{
if (!sender || !receiver || !slotObj || !senderMetaObject) {
qWarning("QObject::connect: invalid null parameter");
if (slotObj)
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
QObject *s = const_cast(sender);
QObject *r = const_cast(receiver);
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
if (type & Qt::UniqueConnection && slot) {
QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
if (connectionLists && connectionLists->count() > signal_index) {
const QObjectPrivate::Connection *c2 =
(*connectionLists)[signal_index].first;
while (c2) {
if (c2->receiver == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
c2 = c2->nextConnectionList;
}
}
type = static_cast(type ^ Qt::UniqueConnection);
}
// QObjectPrivate::Connection实例化,
//存储了信号-槽链接的信息
QScopedPointer c(new QObjectPrivate::Connection);
c->sender = s;
c->signal_index = signal_index;
c->receiver = r;
c->slotObj = slotObj;
c->connectionType = type;
c->isSlotObject = true;
if (types) {
c->argumentTypes.store(types);
c->ownArgumentTypes = false;
}
//addConnection为信号发送者s保存了这个信号-槽链接,具体保存了什么,还需要
//分析QObjectPrivate::Connection以及QObjectPrivate::addConnection
QObjectPrivate::get(s)->addConnection(signal_index, c.data());
QMetaObject::Connection ret(c.take());
locker.unlock();
QMetaMethod method = QMetaObjectPrivate::signal(senderMetaObject, signal_index);
Q_ASSERT(method.isValid());
s->connectNotify(method);
return ret;
}
qobject_p.h中定义了class QObjectPrivate
class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{
Q_DECLARE_PUBLIC(QObject)
public:
struct ExtraData
{
ExtraData() {}
#ifndef QT_NO_USERDATA
QVector userData;
#endif
QList propertyNames;
QVector propertyValues;
QVector runningTimers;
QList > eventFilters;
QString objectName;
};
typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);
struct Connection
{
QObject *sender;
QObject *receiver;
union {
StaticMetaCallFunction callFunction;
QtPrivate::QSlotObjectBase *slotObj;
};
// The next pointer for the singly-linked ConnectionList
Connection *nextConnectionList;
//senders linked list
Connection *next;
Connection **prev;
QAtomicPointer argumentTypes;
QAtomicInt ref_;
ushort method_offset;
ushort method_relative;
uint signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())
ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
ushort isSlotObject : 1;
ushort ownArgumentTypes : 1;
Connection() : nextConnectionList(0), ref_(2), ownArgumentTypes(true) {
//ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection
}
~Connection();
int method() const { Q_ASSERT(!isSlotObject); return method_offset + method_relative; }
void ref() { ref_.ref(); }
void deref() {
if (!ref_.deref()) {
Q_ASSERT(!receiver);
delete this;
}
}
};
// ConnectionList is a singly-linked list
struct ConnectionList {
ConnectionList() : first(0), last(0) {}
Connection *first;
Connection *last;
};
struct Sender
{
QObject *sender;
int signal;
int ref;
};
QObjectPrivate(int version = QObjectPrivateVersion);
virtual ~QObjectPrivate();
void deleteChildren();
void setParent_helper(QObject *);
void moveToThread_helper();
void setThreadData_helper(QThreadData *currentData, QThreadData *targetData);
void _q_reregisterTimers(void *pointer);
bool isSender(const QObject *receiver, const char *signal) const;
QObjectList receiverList(const char *signal) const;
QObjectList senderList() const;
void addConnection(int signal, Connection *c);
void cleanConnectionLists();
static inline Sender *setCurrentSender(QObject *receiver,
Sender *sender);
static inline void resetCurrentSender(QObject *receiver,
Sender *currentSender,
Sender *previousSender);
static QObjectPrivate *get(QObject *o) {
return o->d_func();
}
static const QObjectPrivate *get(const QObject *o) { return o->d_func(); }
int signalIndex(const char *signalName, const QMetaObject **meta = 0) const;
inline bool isSignalConnected(uint signalIdx, bool checkDeclarative = true) const;
inline bool isDeclarativeSignalConnected(uint signalIdx) const;
// To allow abitrary objects to call connectNotify()/disconnectNotify() without making
// the API public in QObject. This is used by QQmlNotifierEndpoint.
inline void connectNotify(const QMetaMethod &signal);
inline void disconnectNotify(const QMetaMethod &signal);
template
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal,
const typename QtPrivate::FunctionPointer::Object *receiverPrivate, Func2 slot,
Qt::ConnectionType type = Qt::AutoConnection);
template
static inline bool disconnect(const typename QtPrivate::FunctionPointer::Object *sender, Func1 signal,
const typename QtPrivate::FunctionPointer::Object *receiverPrivate, Func2 slot);
static QMetaObject::Connection connectImpl(const QObject *sender, int signal_index,
const QObject *receiver, void **slot,
QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type,
const int *types, const QMetaObject *senderMetaObject);
static QMetaObject::Connection connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type);
static bool disconnect(const QObject *sender, int signal_index, void **slot);
public:
ExtraData *extraData; // extra data set by the user
QThreadData *threadData; // id of the thread that owns the object
QObjectConnectionListVector *connectionLists;
Connection *senders; // linked list of connections connected to this object
Sender *currentSender; // object currently activating the object
mutable quint32 connectedSignals[2];
union {
QObject *currentChildBeingDeleted;
QAbstractDeclarativeData *declarativeData; //extra data used by the declarative module
};
// these objects are all used to indicate that a QObject was deleted
// plus QPointer, which keeps a separate list
QAtomicPointer sharedRefcount;
};
Connection结构体保存了一个连接。其中的信息包括信号发送者指针,信号接收者指针以及指向下一个Connection的指针,信号索引,连接类型等
一个信号可以对应多个槽函数,这里用ConnectionList保存一个信号对应的所有连接。它是一个单向链表,每个节点都是一个Connection,通过它内部的nextConnectionList指针指向下一个Connection。在这里仅保存头尾指针即可:
// ConnectionList is a singly-linked list
struct ConnectionList {
ConnectionList() : first(0), last(0) {}
Connection *first;
Connection *last;
};
connectionLists保存此对象作为信号发送者所对应的所有连接。这个向量里每个元素都是一个ConnectionList单链表
QObjectConnectionListVector *connectionLists;
QObject.cpp里定义了QObjectConnectionListVector
/*
This vector contains the all connections from an object.
Each object may have one vector containing the lists of
connections for a given signal. The index in the vector correspond
to the signal index. The signal index is the one returned by
QObjectPrivate::signalIndex (not QMetaObject::indexOfSignal).
Negative index means connections to all signals.
This vector is protected by the object mutex (signalSlotMutexes())
Each Connection is also part of a 'senders' linked list. The mutex
of the receiver must be locked when touching the pointers of this
linked list.
*/
class QObjectConnectionListVector : public QVector
{
public:
bool orphaned; //the QObject owner of this vector has been destroyed while the vector was inUse
bool dirty; //some Connection have been disconnected (their receiver is 0) but not removed from the list yet
int inUse; //number of functions that are currently accessing this object or its connections
QObjectPrivate::ConnectionList allsignals;
QObjectConnectionListVector()
: QVector(), orphaned(false), dirty(false), inUse(0)
{ }
QObjectPrivate::ConnectionList &operator[](int at)
{
if (at < 0)
return allsignals;
return QVector::operator[](at);
}
};
void QObjectPrivate::addConnection(int signal, Connection *c)
{
Q_ASSERT(c->sender == q_ptr);
if (!connectionLists)
connectionLists = new QObjectConnectionListVector();//没有则创建
if (signal >= connectionLists->count())
connectionLists->resize(signal + 1);
//根据信号索引取得此信号所对应的链表,并把此连接加入链表中。
ConnectionList &connectionList = (*connectionLists)[signal];
if (connectionList.last) {
connectionList.last->nextConnectionList = c;
}else {
connectionList.first = c;
}
connectionList.last = c;
cleanConnectionLists();
//下面是对Connection* QObjectPrivate::senders双向链表的操作
c->prev = &(QObjectPrivate::get(c->receiver)->senders);
c->next = *c->prev;
*c->prev = c;
if (c->next)
c->next->prev = &c->next;
if (signal < 0) {
connectedSignals[0] = connectedSignals[1] = ~0;
}else if (signal < (int)sizeof(connectedSignals) * 8) {
connectedSignals[signal >> 5] |= (1 << (signal & 0x1f));
}
}
/*!
\internal
*/
void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)
{
int signal_index = signalOffset + local_signal_index;
if (sender->d_func()->blockSig)
return;
if (sender->d_func()->isDeclarativeSignalConnected(signal_index)
&& QAbstractDeclarativeData::signalEmitted) {
QAbstractDeclarativeData::signalEmitted(sender->d_func()->declarativeData, sender,
signal_index, argv);
}
//判断是否有与该信号相连接的接收对象
if (!sender->d_func()->isSignalConnected(signal_index, /*checkDeclarative =*/ false)
&& !qt_signal_spy_callback_set.signal_begin_callback
&& !qt_signal_spy_callback_set.signal_end_callback) {
// The possible declarative connection is done, and nothing else is connected, so:
return;
}
void *empty_argv[] = { 0 };
if (qt_signal_spy_callback_set.signal_begin_callback != 0) {
qt_signal_spy_callback_set.signal_begin_callback(sender, signal_index,
argv ? argv : empty_argv);
}
{
//给信号量加锁,因为在connectionLists里所有的操作都是线程安全的
QMutexLocker locker(signalSlotLock(sender));
struct ConnectionListsRef {
QObjectConnectionListVector *connectionLists;
ConnectionListsRef(QObjectConnectionListVector *connectionLists) : connectionLists(connectionLists)
{
if (connectionLists)
++connectionLists->inUse;
}
~ConnectionListsRef()
{
if (!connectionLists)
return;
--connectionLists->inUse;
Q_ASSERT(connectionLists->inUse >= 0);
if (connectionLists->orphaned) {
if (!connectionLists->inUse)
delete connectionLists;
}
}
QObjectConnectionListVector *operator->() const { return connectionLists; }
};
//获取与该信号的ConnectionList链表
ConnectionListsRef connectionLists = sender->d_func()->connectionLists;
if (!connectionLists.connectionLists) {
locker.unlock();
if (qt_signal_spy_callback_set.signal_end_callback != 0)
qt_signal_spy_callback_set.signal_end_callback(sender, signal_index);
return;
}
const QObjectPrivate::ConnectionList *list;
if (signal_index < connectionLists->count())
list = &connectionLists->at(signal_index);
else
list = &connectionLists->allsignals;
Qt::HANDLE currentThreadId = QThread::currentThreadId();
//循环执行该信号对应的所有槽函数
do {
QObjectPrivate::Connection *c = list->first;
if (!c) continue;
// We need to check against last here to ensure that signals added
// during the signal emission are not emitted in this emission.
QObjectPrivate::Connection *last = list->last;
do {
if (!c->receiver)
continue;
QObject * const receiver = c->receiver;
const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId.load();
// determine if this connection should be sent immediately or
// put into the event queue
// 决定该连接是马上响应还是把它放到事件队列中
//1 Qt::AutoConnection 并且接收者不在一个线程 或者是 Qt::QueuedConnection 放入事件队列,
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
|| (c->connectionType == Qt::QueuedConnection)) {
queued_activate(sender, signal_index, c, argv ? argv : empty_argv, locker);
continue;
#ifndef QT_NO_THREAD
} else if (c->connectionType == Qt::BlockingQueuedConnection) {
if (receiverInSameThread) {
qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "
"Sender is %s(%p), receiver is %s(%p)",
sender->metaObject()->className(), sender,
receiver->metaObject()->className(), receiver);
}
QSemaphore semaphore;
QMetaCallEvent *ev = c->isSlotObject ?
new QMetaCallEvent(c->slotObj, sender, signal_index, 0, 0, argv ? argv : empty_argv, &semaphore) :
new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal_index, 0, 0, argv ? argv : empty_argv, &semaphore);
QCoreApplication::postEvent(receiver, ev);
locker.unlock();
semaphore.acquire();
locker.relock();
continue;
#endif
}
QConnectionSenderSwitcher sw;
if (receiverInSameThread) {
sw.switchSender(receiver, sender, signal_index);
}
//下面if-else结构包含三种调用槽函数的方式。
if (c->isSlotObject) {
c->slotObj->ref();
QScopedPointer obj(c->slotObj);
locker.unlock();
//一,通过call调用接收者中的槽函数
obj->call(receiver, argv ? argv : empty_argv);
// Make sure the slot object gets destroyed before the mutex is locked again, as the
// destructor of the slot object might also lock a mutex from the signalSlotLock() mutex pool,
// and that would deadlock if the pool happens to return the same mutex.
obj.reset();
locker.relock();
} else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
//we compare the vtable to make sure we are not in the destructor of the object.
const int methodIndex = c->method();
const int method_relative = c->method_relative;
const auto callFunction = c->callFunction;
locker.unlock();
if (qt_signal_spy_callback_set.slot_begin_callback != 0)
qt_signal_spy_callback_set.slot_begin_callback(receiver, methodIndex, argv ? argv : empty_argv);
//二,callFunction即moc_test.cpp里的qt_static_metacall
callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv ? argv : empty_argv);
if (qt_signal_spy_callback_set.slot_end_callback != 0)
qt_signal_spy_callback_set.slot_end_callback(receiver, methodIndex);
locker.relock();
} else {
const int method = c->method_relative + c->method_offset;
locker.unlock();
if (qt_signal_spy_callback_set.slot_begin_callback != 0) {
qt_signal_spy_callback_set.slot_begin_callback(receiver,
method,
argv ? argv : empty_argv);
}
//三,metacall即moc_test.cpp里的qt_metacall
metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv ? argv : empty_argv);
if (qt_signal_spy_callback_set.slot_end_callback != 0)
qt_signal_spy_callback_set.slot_end_callback(receiver, method);
locker.relock();
}
if (connectionLists->orphaned)
break;
} while (c != last && (c = c->nextConnectionList) != 0);
if (connectionLists->orphaned)
break;
} while (list != &connectionLists->allsignals &&
//start over for all signals;
((list = &connectionLists->allsignals), true));
}
if (qt_signal_spy_callback_set.signal_end_callback != 0)
qt_signal_spy_callback_set.signal_end_callback(sender, signal_index);
}
关联到某个信号的所有槽都在这里的得到处理,要么立即被调用,这是发送者和接收者在同一个线程中时的默认处理方式;要么列队发送,这是发送者和接收者不在同一个线程中时的默认处理方式。
Native widget or Alien widget
Native widget是指拥有windows窗口句柄的widget,占用了内核资源,Alien widget是指依附在某个Native widget上的子窗口,没有windows窗口句柄,不占用内核资源。在qt4.4之前,所有的widget都是Native widget,而且伴随着被人诟病的 闪烁现象 。qt4.4之后,使用了Alien widget,子窗口默认是一个Alien widget,除非必要,qt不会创建一个Native widget。qt想尽可能地把对窗口的处理从内核转移到qt上,从而拥有更大的自主权。widget的Alien widget和Native widget属性是可以配置的,如果你确实需要一个Native widget,你可以对属性进行显示的配置:
setAttribute(Qt::WA_NativeWindow);
创建Native widget
如果一个窗口是Alien widget,没有窗口句柄,那他怎么得到windows的消息呢?不能,可以肯定的是一个没有窗口句柄的Alien widget是无法被操作系统感知的。所以,在windows之上,qt建立了自己的窗口系统。然而,qt程序逃避不了的一个事实是:它需要获取windows的消息。要获取操作系统消息的前提是拥有窗口句柄,所以,qt的窗口程序一般是这样的:
创建一个Native widget就意味着需要注册一个窗口类或者使用同名的已经注册过的窗口类,然后创建一个内核窗口对象,并返回窗口句柄。
QWidget注册窗口类时使用的窗口过程为 qWindowsWndProc
:
例如,当你点击窗口时,windows感知到了用户对native widget的点击操作,并将消息发送给native widget所在的线程的消息队列,在qt的消息循环中能够GetMessage获取消息,qt使用GetMessage的非阻塞版PeekMessage获取线程队列中的消息,然后通过DispatchMessage将消息转给Native widget的这个窗口过程 qWindowsWndProc
。
窗口过程将消息映射到系统事件类型WindowsEventType,case各种类型,最后将封装好的WindowSystemEvent格式的系统事件放入QWindowSystemInterfacePrivate的全局系统事件队列中
对,一般都是将事件放入windowSystemEventQueue队列中排队。然后就一步步的返回了,最后从DispatchMessage回到事件循环。
如果遇到qt不干兴趣的消息,他不会将其放入windowSystemEventQueue队列,而是从windowsProc返回false,交给DefWindowProc进行默认处理。
在定义一个QApplication的变量时,构造函数就在内部创建一个专门用来处理qt事件的message-only窗口,这个窗口有窗口句柄,但是不显示。这个窗口类注册的窗口过程是 qt_internal_proc
, 通过qt_create_internal_window创建,整个流程是这样的:
qt事件循环需要维护一个事件队列,在Qt的main函数中最后一般调用QApplication::exec()成员函数来保持程序对事件队列的处理,exec()的实质是不停调用processEvent()函数从队列中获取事件,并处理,然后删除,postEvent的作用就是发送一个事件到此队列中,由于删除队列中事件调用delete运算符,所以,postEvent()传递的事件一定要是动态分配的。
sendEvent()函数直接跳过事件循环队列,直接调用notify()函数发送事件到目标对象,并等待事件处理结果,所以其传递的事件直接在栈上分配即可,(postEvent()函数只负责添加事件到队列,不等待事件处理结果)。
信号槽的连接方式是QueuedConnection时
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread) || (c->connectionType == Qt::QueuedConnection)) {
queued_activate(sender, signal_index, c, argv);
continue;
#if QT_CONFIG(thread)
}
上述代码表明:当信号槽的连接方式是QueuedConnection或者使用Qt::AutoConnection但是不在一个线程时,直接调用的是queued_activate,然后 continue 进行下次循环
static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
{
const int *argumentTypes = c->argumentTypes.loadRelaxed();//获取connection中的信号函数的参数类型
if (!argumentTypes) {//如果信号函数的参数类型为空,重新获取
QMetaMethod m = QMetaObjectPrivate::signal(sender->metaObject(), signal);//根据发送信号的元对象和和信号索引获取信号函数
argumentTypes = queuedConnectionTypes(m.parameterTypes());//将信号函数的参数封装在一个数组中并返回数组的首地址
if (!argumentTypes) // cannot queue arguments//异常检测
argumentTypes = &DIRECT_CONNECTION_ONLY;
if (!c->argumentTypes.testAndSetOrdered(0, argumentTypes)) {
if (argumentTypes != &DIRECT_CONNECTION_ONLY)
delete [] argumentTypes;
argumentTypes = c->argumentTypes.loadRelaxed();
}
}
if (argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate//异常检测
return;
int nargs = 1; // include return type
while (argumentTypes[nargs-1])//获取信号参数数组中参数的个数
++nargs;
QBasicMutexLocker locker(signalSlotLock(c->receiver.loadRelaxed()));
if (!c->receiver.loadRelaxed()) {
// the connection has been disconnected before we got the lock
return;
}
if (c->isSlotObject)
c->slotObj->ref();
locker.unlock();
QMetaCallEvent *ev = c->isSlotObject ?//如果connection中包含一个有用槽函数指针的对象,那么,对应QT5的新语法
//如果connection中仅仅是一个函数指针callFunction,对应QT4语法,这样就能让QT同时支持两种语法
new QMetaCallEvent(c->slotObj, sender, signal, nargs) :
new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs);
void **args = ev->args();
int *types = ev->types();
types[0] = 0; // return type
args[0] = nullptr; // return value
if (nargs > 1) {
for (int n = 1; n < nargs; ++n)
types[n] = argumentTypes[n-1];//将argumentTypes中的参数类型填充到QMetaCallEvent中
for (int n = 1; n < nargs; ++n)
args[n] = QMetaType::create(types[n], argv[n]);//利用参数类型和参数的序号找到参数对应的值,然后填充到QMetaCallEvent中
}
//填充到QMetaCallEvent中的参数类型和参数值主要是因为:信号参数的指针数组在栈上。一旦信号退出,这些指针数组将不再有效。
locker.relock();
if (c->isSlotObject)
c->slotObj->destroyIfLastRef();
if (!c->receiver.loadRelaxed()) {
// the connection has been disconnected while we were unlocked
locker.unlock();
delete ev;
return;
}
post事件到接收者所属线程的QThreadData::postEventList队列中
QCoreApplication::postEvent(c->receiver.loadRelaxed(), ev);//将拥有参数类型和参数值的QMetaCallEvent放到事件队列中
}
所以,queued_activate的基本思路就是将信号的参数信息放到QMetaCallEvent中,然后放到循环事件队列中等待发送
QMetaCallEvent的定义如下:
class Q_CORE_EXPORT QMetaCallEvent : public QAbstractMetaCallEvent
{
public:
// blocking queued with semaphore - args always owned by caller
QMetaCallEvent(ushort method_offset, ushort method_relative,
QObjectPrivate::StaticMetaCallFunction callFunction,
const QObject *sender, int signalId,
void **args, QSemaphore *semaphore);
QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj,
const QObject *sender, int signalId,
void **args, QSemaphore *semaphore);
// queued - args allocated by event, copied by caller
QMetaCallEvent(ushort method_offset, ushort method_relative,
QObjectPrivate::StaticMetaCallFunction callFunction,
const QObject *sender, int signalId,
int nargs);
QMetaCallEvent(QtPrivate::QSlotObjectBase *slotObj,
const QObject *sender, int signalId,
int nargs);
//四个构造函数分别对象QT4和QT5的语法,并且分别对应QueuedConnection和BlockingQueuedConnection的连接方式
~QMetaCallEvent() override;
inline int id() const { return d.method_offset_ + d.method_relative_; }
inline const void * const* args() const { return d.args_; }//该函数返回一个const指针,该const指针指向一个const void的指针
inline void ** args() { return d.args_; }
inline const int *types() const { return reinterpret_cast(d.args_ + d.nargs_); }
inline int *types() { return reinterpret_cast(d.args_ + d.nargs_); }
virtual void placeMetaCall(QObject *object) override;
private:
inline void allocArgs();
struct Data {
QtPrivate::QSlotObjectBase *slotObj_;//成员槽函数指针的对象的指针
void **args_;//指向存储参数指针的数组的二级指针
QObjectPrivate::StaticMetaCallFunction callFunction_;//函数指针,就是MOC文件中的qt_static_metacall
int nargs_;//参数的个数
ushort method_offset_;//信号方法的偏移
ushort method_relative_;//信号方法的相对序号
} d;
// preallocate enough space for three arguments
char prealloc_[3*(sizeof(void*) + sizeof(int))];
};
QCoreApplication::postEvent的实现如下
void QCoreApplication::postEvent(QObject *receiver, QEvent *event, int priority)
{
Q_TRACE_SCOPE(QCoreApplication_postEvent, receiver, event, event->type());
if (receiver == 0) {//异常处理
qWarning("QCoreApplication::postEvent: Unexpected null receiver");
delete event;
return;
}
QThreadData * volatile * pdata = &receiver->d_func()->threadData;
QThreadData *data = *pdata;//获取接收者所在的线程数据
if (!data) {
// posting during destruction? just delete the event to prevent a leak
delete event;
return;
}
// lock the post event mutex
data->postEventList.mutex.lock();
//事件将发送到每个线程的事件队列(QThreadData::postEventList)中。
//排队的事件受互斥锁保护,因此当线程将事件推送到另一个线程的事件队列时,没有竞争条件。
// if object has moved to another thread, follow it//接收者有可能被移动到其他线程中运行,那么将线程数据的指针重新指向
while (data != *pdata) {
data->postEventList.mutex.unlock();
data = *pdata;
if (!data) {
// posting during destruction? just delete the event to prevent a leak
delete event;
return;
}
data->postEventList.mutex.lock();
}
QMutexUnlocker locker(&data->postEventList.mutex);
// if this is one of the compressible events, do compression
if (receiver->d_func()->postedEvents
&& self && self->compressEvent(event, receiver, &data->postEventList)) {
Q_TRACE(QCoreApplication_postEvent_event_compressed, receiver, event);
return;
}
//compressEvent作用是判断一个事件是否已经被删除,如果被删除,则不应该添加到事件队列中
if (event->type() == QEvent::DeferredDelete)//关于DeferredDelete事件的处理,暂时不用管
receiver->d_ptr->deleteLaterCalled = true;
if (event->type() == QEvent::DeferredDelete && data == QThreadData::current()) {//关于DeferredDelete事件的处理,暂时不用管
// remember the current running eventloop for DeferredDelete
// events posted in the receiver's thread.
// Events sent by non-Qt event handlers (such as glib) may not
// have the scopeLevel set correctly. The scope level makes sure that
// code like this:
// foo->deleteLater();
// qApp->processEvents(); // without passing QEvent::DeferredDelete
// will not cause "foo" to be deleted before returning to the event loop.
// If the scope level is 0 while loopLevel != 0, we are called from a
// non-conformant code path, and our best guess is that the scope level
// should be 1. (Loop level 0 is special: it means that no event loops
// are running.)
int loopLevel = data->loopLevel;
int scopeLevel = data->scopeLevel;
if (scopeLevel == 0 && loopLevel != 0)
scopeLevel = 1;
static_cast(event)->level = loopLevel + scopeLevel;
}
// delete the event on exceptions to protect against memory leaks till the event is
// properly owned in the postEventList
QScopedPointer eventDeleter(event);//在处理事件的线程中对其进行处理后,将立即删除该事件
Q_TRACE(QCoreApplication_postEvent_event_posted, receiver, event, event->type());
data->postEventList.addEvent(QPostEvent(receiver, event, priority));//将事件放入循环队列中
eventDeleter.take();
event->posted = true;
++receiver->d_func()->postedEvents;
data->canWait = false;
locker.unlock();
QAbstractEventDispatcher* dispatcher = data->eventDispatcher.loadAcquire();//取得eventDispatcher,Linux下对应的是QEventDispatcherGlib的指针
if (dispatcher)
dispatcher->wakeUp();//唤醒事件循环迭代上下文mainContext
}
void QEventDispatcherGlib::wakeUp()
{
Q_D(QEventDispatcherGlib);
d->postEventSource->serialNumber.ref();
g_main_context_wakeup(d->mainContext);
}
所以,postEvent实际上是将事件放到 data->postEventList.addEvent(QPostEvent(receiver, event, priority));//将事件放入循环队列中对应线程的事件循环中,所以当我们使用QueuedConnection对信号槽进行连接时,会先执行信号函数所在线程的代码,槽函数此时在对应线程的循环队列中等待,等信号函数所在线程的循环队列没有事件时,再执行槽函数所在线程的代码
可以看到,跨线程的槽调用不是同步的,而是异步的调用,保证了一个对象的槽函数一定会在这个对象所属的线程中执行,而不是其他线程。这里ob属于t线程,所以ob的槽函数就是在t线程中执行的。
实际上,跨线程的槽是以事件的形式异步调用的,调用事件的派发使用
void QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type, QThreadData *data)
就像在GUI线程中的事件循环需要使用一个 事件派发器 一样,任何一个线程中的事件循环都需要一个派发器。GUI线程中的事件派发器是在构造QApplication时创建的,是一个QWindowsGuiEventDispatcher类的派发器,在这个派发器的构造函数中同时还创建了一个message-only窗口。
对于需要事件循环的非GUI线程,message-only窗口是不可或缺的,因为没有他,线程就没有消息队列,何谈消息循环,除非Qt使用另外的机制而非消息循环机制来支持非GUI线程的事件循环,不过这完全没必要。我们来看看这些步骤在非GUI线程中是怎么完成的:
创建的是一个QEventDispatcherWin32类的事件派发器,它并不像QWindowsGuiEventDispatcher一样在构造的同时还创建 message-only 窗口。
第二步,创建一个 message-only 窗口
如果在启动事件循环的过程中发现当前的事件派发器还没有创建 message-only 窗口的话,那就会为其创建一个这样的窗口。
对比GUI线程创建事件派发器和message-only窗口的一步到位,非GUI线程采用延迟的方式来处理。为什么要这样做呢?像GUI线程一步到位不行吗?当然可以,但是没必要,因为创建一个 message-only 窗口是要占用内核资源的,GUI线程一定需要一个消息循环来实现事件循环,所以一步到位的创建没毛病,但是非GUI线程可能根本就不需要一个事件循环,所以,白白浪费资源干嘛呢
可以看到,跨线程的槽调用不是同步的,而是异步的调用,保证了一个对象的槽函数一定会在这个对象所属的线程中执行,而不是其他线程。这里ob属于t线程,所以ob的槽函数就是在t线程中执行的
实际上,跨线程的槽是以事件的形式异步调用的,调用事件的派发使用:
bool QEventDispatcherWin32::processEvents(QEventLoop::ProcessEventsFlags flags)
{
Q_D(QEventDispatcherWin32);
if (!d->internalHwnd) {
createInternalHwnd();
wakeUp(); // trigger a call to sendPostedEvents()
}
d->interrupt = false;
emit awake();
bool canWait;
bool retVal = false;
bool seenWM_QT_SENDPOSTEDEVENTS = false;
bool needWM_QT_SENDPOSTEDEVENTS = false;
do {
DWORD waitRet = 0;
HANDLE pHandles[MAXIMUM_WAIT_OBJECTS - 1];
QVarLengthArray processedTimers;
while (!d->interrupt) {
DWORD nCount = d->winEventNotifierList.count();
Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
MSG msg;
bool haveMessage;
if (!(flags & QEventLoop::ExcludeUserInputEvents) && !d->queuedUserInputEvents.isEmpty()) {
// process queued user input events
haveMessage = true;
msg = d->queuedUserInputEvents.takeFirst();
} else if(!(flags & QEventLoop::ExcludeSocketNotifiers) && !d->queuedSocketEvents.isEmpty()) {
// process queued socket events
haveMessage = true;
msg = d->queuedSocketEvents.takeFirst();
} else {
haveMessage = PeekMessage(&msg, 0, 0, 0, PM_REMOVE);
if (haveMessage) {
if ((flags & QEventLoop::ExcludeUserInputEvents)
&& ((msg.message >= WM_KEYFIRST
&& msg.message <= WM_KEYLAST)
|| (msg.message >= WM_MOUSEFIRST
&& msg.message <= WM_MOUSELAST)
|| msg.message == WM_MOUSEWHEEL
|| msg.message == WM_MOUSEHWHEEL
|| msg.message == WM_TOUCH
#ifndef QT_NO_GESTURES
|| msg.message == WM_GESTURE
|| msg.message == WM_GESTURENOTIFY
#endif
|| msg.message == WM_CLOSE)) {
// queue user input events for later processing
d->queuedUserInputEvents.append(msg);
continue;
}
if ((flags & QEventLoop::ExcludeSocketNotifiers)
&& (msg.message == WM_QT_SOCKETNOTIFIER && msg.hwnd == d->internalHwnd)) {
// queue socket events for later processing
d->queuedSocketEvents.append(msg);
continue;
}
}
}
if (!haveMessage) {
// no message - check for signalled objects
for (int i=0; i<(int)nCount; i++)
pHandles[i] = d->winEventNotifierList.at(i)->handle();
waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, 0, QS_ALLINPUT, MWMO_ALERTABLE);
if ((haveMessage = (waitRet == WAIT_OBJECT_0 + nCount))) {
// a new message has arrived, process it
continue;
}
}
if (haveMessage) {
// WinCE doesn't support hooks at all, so we have to call this by hand :(
if (!d->getMessageHook)
(void) qt_GetMessageHook(0, PM_REMOVE, (LPARAM) &msg);
if (d->internalHwnd == msg.hwnd && msg.message == WM_QT_SENDPOSTEDEVENTS) {
if (seenWM_QT_SENDPOSTEDEVENTS) {
// when calling processEvents() "manually", we only want to send posted
// events once
needWM_QT_SENDPOSTEDEVENTS = true;
continue;
}
seenWM_QT_SENDPOSTEDEVENTS = true;
} else if (msg.message == WM_TIMER) {
// avoid live-lock by keeping track of the timers we've already sent
bool found = false;
for (int i = 0; !found && i < processedTimers.count(); ++i) {
const MSG processed = processedTimers.constData()[i];
found = (processed.wParam == msg.wParam && processed.hwnd == msg.hwnd && processed.lParam == msg.lParam);
}
if (found)
continue;
processedTimers.append(msg);
} else if (msg.message == WM_QUIT) {
if (QCoreApplication::instance())
QCoreApplication::instance()->quit();
return false;
}
if (!filterNativeEvent(QByteArrayLiteral("windows_generic_MSG"), &msg, 0)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
} else if (waitRet - WAIT_OBJECT_0 < nCount) {
d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
} else {
// nothing todo so break
break;
}
retVal = true;
}
// still nothing - wait for message or signalled objects
canWait = (!retVal
&& !d->interrupt
&& (flags & QEventLoop::WaitForMoreEvents));
if (canWait) {
DWORD nCount = d->winEventNotifierList.count();
Q_ASSERT(nCount < MAXIMUM_WAIT_OBJECTS - 1);
for (int i=0; i<(int)nCount; i++)
pHandles[i] = d->winEventNotifierList.at(i)->handle();
emit aboutToBlock();
waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);
emit awake();
if (waitRet - WAIT_OBJECT_0 < nCount) {
d->activateEventNotifier(d->winEventNotifierList.at(waitRet - WAIT_OBJECT_0));
retVal = true;
}
}
} while (canWait);
//sendPostedEvents
if (!seenWM_QT_SENDPOSTEDEVENTS && (flags & QEventLoop::EventLoopExec) == 0) {
// when called "manually", always send posted events
sendPostedEvents();
}
if (needWM_QT_SENDPOSTEDEVENTS)
PostMessage(d->internalHwnd, WM_QT_SENDPOSTEDEVENTS, 0, 0);
return r
void QEventDispatcherWin32::sendPostedEvents()
{
Q_D(QEventDispatcherWin32);
QCoreApplicationPrivate::sendPostedEvents(0, 0, d->threadData);
}
QEventDispatcherWin32::sendPostedEvents
将Post到当前线程QThreadData::postEventList中的QPostEvent包装着的QEvent发送给QPostEvent**指定的对象**
oid QCoreApplicationPrivate::sendPostedEvents(QObject *receiver, int event_type,
QThreadData *data)
{
if (event_type == -1) {
// we were called by an obsolete event dispatcher.
event_type = 0;
}
if (receiver && receiver->d_func()->threadData != data) {
qWarning("QCoreApplication::sendPostedEvents: Cannot send "
"posted events for objects in another thread");
return;
}
++data->postEventList.recursion;
QMutexLocker locker(&data->postEventList.mutex);
// by default, we assume that the event dispatcher can go to sleep after
// processing all events. if any new events are posted while we send
// events, canWait will be set to false.
data->canWait = (data->postEventList.size() == 0);
if (data->postEventList.size() == 0 || (receiver && !receiver->d_func()->postedEvents)) {
--data->postEventList.recursion;
return;
}
data->canWait = true;
// okay. here is the tricky loop. be careful about optimizing
// this, it looks the way it does for good reasons.
int startOffset = data->postEventList.startOffset;
int &i = (!event_type && !receiver) ? data->postEventList.startOffset : startOffset;
data->postEventList.insertionOffset = data->postEventList.size();
// Exception-safe cleaning up without the need for a try/catch block
struct CleanUp {
QObject *receiver;
int event_type;
QThreadData *data;
bool exceptionCaught;
inline CleanUp(QObject *receiver, int event_type, QThreadData *data) :
receiver(receiver), event_type(event_type), data(data), exceptionCaught(true)
{}
inline ~CleanUp()
{
if (exceptionCaught) {
// since we were interrupted, we need another pass to make sure we clean everything up
data->canWait = false;
}
--data->postEventList.recursion;
if (!data->postEventList.recursion && !data->canWait && data->hasEventDispatcher())
data->eventDispatcher.load()->wakeUp();
// clear the global list, i.e. remove everything that was
// delivered.
if (!event_type && !receiver && data->postEventList.startOffset >= 0) {
const QPostEventList::iterator it = data->postEventList.begin();
data->postEventList.erase(it, it + data->postEventList.startOffset);
data->postEventList.insertionOffset -= data->postEventList.startOffset;
Q_ASSERT(data->postEventList.insertionOffset >= 0);
data->postEventList.startOffset = 0;
}
}
};
CleanUp cleanup(receiver, event_type, data);
while (i < data->postEventList.size()) {
// avoid live-lock
if (i >= data->postEventList.insertionOffset)
break;
const QPostEvent &pe = data->postEventList.at(i);
++i;
if (!pe.event)
continue;
if ((receiver && receiver != pe.receiver) || (event_type && event_type != pe.event->type())) {
data->canWait = false;
continue;
}
if (pe.event->type() == QEvent::DeferredDelete) {
// DeferredDelete events are sent either
// 1) when the event loop that posted the event has returned; or
// 2) if explicitly requested (with QEvent::DeferredDelete) for
// events posted by the current event loop; or
// 3) if the event was posted before the outermost event loop.
int eventLevel = static_cast(pe.event)->loopLevel();
int loopLevel = data->loopLevel + data->scopeLevel;
const bool allowDeferredDelete =
(eventLevel > loopLevel
|| (!eventLevel && loopLevel > 0)
|| (event_type == QEvent::DeferredDelete
&& eventLevel == loopLevel));
if (!allowDeferredDelete) {
// cannot send deferred delete
if (!event_type && !receiver) {
// we must copy it first; we want to re-post the event
// with the event pointer intact, but we can't delay
// nulling the event ptr until after re-posting, as
// addEvent may invalidate pe.
QPostEvent pe_copy = pe;
// null out the event so if sendPostedEvents recurses, it
// will ignore this one, as it's been re-posted.
const_cast(pe).event = 0;
// re-post the copied event so it isn't lost
data->postEventList.addEvent(pe_copy);
}
continue;
}
}
// first, we diddle the event so that we can deliver
// it, and that no one will try to touch it later.
pe.event->posted = false;
QEvent *e = pe.event;
QObject * r = pe.receiver;
--r->d_func()->postedEvents;
Q_ASSERT(r->d_func()->postedEvents >= 0);
// next, update the data structure so that we're ready
// for the next event.
const_cast(pe).event = 0;
struct MutexUnlocker
{
QMutexLocker &m;
MutexUnlocker(QMutexLocker &m) : m(m) { m.unlock(); }
~MutexUnlocker() { m.relock(); }
};
MutexUnlocker unlocker(locker);
QScopedPointer event_deleter(e); // will delete the event (with the mutex unlocked)
// after all that work, it's time to deliver the event.
QCoreApplication::sendEvent(r, e);
// careful when adding anything below this point - the
// sendEvent() call might invalidate any invariants this
// function depends on.
}
cleanup.exceptionCaught = false;
}
走sendevent:
inline bool QCoreApplication::sendEvent(QObject *receiver, QEvent *event)
{ if (event) event->spont = false; return notifyInternal2(receiver, event); }
QCoreApplication::notifyInternal2
bool QCoreApplication::notifyInternal2(QObject *receiver, QEvent *event)
{
bool selfRequired = QCoreApplicationPrivate::threadRequiresCoreApplication();
if (!self && selfRequired)
return false;
// Make it possible for Qt Script to hook into events even
// though QApplication is subclassed...
bool result = false;
void *cbdata[] = { receiver, event, &result };
if (QInternal::activateCallbacks(QInternal::EventNotifyCallback, cbdata)) {
return result;
}
// Qt enforces the rule that events can only be sent to objects in
// the current thread, so receiver->d_func()->threadData is
// equivalent to QThreadData::current(), just without the function
// call overhead.
QObjectPrivate *d = receiver->d_func();
QThreadData *threadData = d->threadData;
QScopedScopeLevelCounter scopeLevelCounter(threadData);
if (!selfRequired)
return doNotify(receiver, event);
return self->notify(receiver, event);
}
然后调用notify:这个接口很重要:
bool QApplication::notify(QObject *receiver, QEvent *e)
{
Q_D(QApplication);
// no events are delivered after ~QCoreApplication() has started
if (QApplicationPrivate::is_app_closing)
return true;
if (Q_UNLIKELY(!receiver)) { // serious error
qWarning("QApplication::notify: Unexpected null receiver");
return true;
}
#ifndef QT_NO_DEBUG
d->checkReceiverThread(receiver);
#endif
if (receiver->isWindowType())
QGuiApplicationPrivate::sendQWindowEventToQPlatformWindow(static_cast(receiver), e);
if(e->spontaneous()) {
// Capture the current mouse and keyboard states. Doing so here is
// required in order to support Qt Test synthesized events. Real mouse
// and keyboard state updates from the platform plugin are managed by
// QGuiApplicationPrivate::process(Mouse|Wheel|Key|Touch|Tablet)Event();
switch (e->type()) {
case QEvent::MouseButtonPress:
{
QMouseEvent *me = static_cast(e);
QApplicationPrivate::modifier_buttons = me->modifiers();
QApplicationPrivate::mouse_buttons |= me->button();
break;
}
case QEvent::MouseButtonDblClick:
{
QMouseEvent *me = static_cast(e);
QApplicationPrivate::modifier_buttons = me->modifiers();
QApplicationPrivate::mouse_buttons |= me->button();
break;
}
case QEvent::MouseButtonRelease:
{
QMouseEvent *me = static_cast(e);
QApplicationPrivate::modifier_buttons = me->modifiers();
QApplicationPrivate::mouse_buttons &= ~me->button();
break;
}
case QEvent::KeyPress:
case QEvent::KeyRelease:
case QEvent::MouseMove:
#if QT_CONFIG(wheelevent)
case QEvent::Wheel:
#endif
case QEvent::TouchBegin:
case QEvent::TouchUpdate:
case QEvent::TouchEnd:
#if QT_CONFIG(tabletevent)
case QEvent::TabletMove:
case QEvent::TabletPress:
case QEvent::TabletRelease:
......
case QEvent::Leave:
if (receiver->isWidgetType()) {
QWidget *w = static_cast(receiver);
if (w->testAttribute(Qt::WA_AcceptTouchEvents)) {
RegisterTouchWindowFn registerTouchWindow = reinterpret_cast
(platformNativeInterface()->nativeResourceFunctionForIntegration("registertouchwindow"));
if (registerTouchWindow)
registerTouchWindow(w->window()->windowHandle(), false);
}
}
res = d->notify_helper(receiver, e);
break;
#endif
default:
res = d->notify_helper(receiver, e);
break;
}
核心是根据事件的不同进行分发,如果是信号,走default,执行:
res = d->notify_helper(receiver, e);
bool QApplicationPrivate::notify_helper(QObject *receiver, QEvent * e)
{
// send to all application event filters 一级过滤器
if (threadRequiresCoreApplication()
&& receiver->d_func()->threadData->thread == mainThread()
&& sendThroughApplicationEventFilters(receiver, e))
return true;
if (receiver->isWidgetType()) {
QWidget *widget = static_cast(receiver);
#if !defined(QT_NO_CURSOR)
// toggle HasMouse widget state on enter and leave
if ((e->type() == QEvent::Enter || e->type() == QEvent::DragEnter) &&
(!QApplication::activePopupWidget() || QApplication::activePopupWidget() == widget->window()))
widget->setAttribute(Qt::WA_UnderMouse, true);
else if (e->type() == QEvent::Leave || e->type() == QEvent::DragLeave)
widget->setAttribute(Qt::WA_UnderMouse, false);
#endif
if (QLayout *layout=widget->d_func()->layout) {
layout->widgetEvent(e);
}
}
// send to all receiver event filters 二级过滤器
if (sendThroughObjectEventFilters(receiver, e))
return true;
// deliver the event
bool consumed = receiver->event(e); //事件
QCoreApplicationPrivate::setEventSpontaneous(e, false);
return consumed;
}
然后走到:
bool QObject::event(QEvent *e)
{
//..........
case QEvent::MetaCall:
{
QAbstractMetaCallEvent *mce = static_cast(e);
if (!d_func()->connections.loadRelaxed()) {
QBasicMutexLocker locker(signalSlotLock(this));
d_func()->ensureConnectionData();
}
QObjectPrivate::Sender sender(this, const_cast(mce->sender()), mce->signalId());
mce->placeMetaCall(this);
break;
}
//..........
}
上述代码的核心就是那句mce->placeMetaCall(this);
而placeMetaCall是个纯虚函数,在子类QMetaCallEvent中实现,代码如下
void QMetaCallEvent::placeMetaCall(QObject *object)
{
if (d.slotObj_) {
d.slotObj_->call(object, d.args_);
} else if (d.callFunction_ && d.method_offset_ <= object->metaObject()->methodOffset()) {
d.callFunction_(object, QMetaObject::InvokeMetaMethod, d.method_relative_, d.args_);
} else {
QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod,
d.method_offset_ + d.method_relative_, d.args_);
}
}
可见,当connect是对应QT5的语法时,调用的是d.slotObj_->call(object, d.args_);,直接进行函数调用,而当connect是对应QT4的语法时,直接调用函数指针,而函数指针指向的是MOC文件中的qt_static_metacall,具体见博客https://blog.csdn.net/Master_Cui/article/details/109011153和https://blog.csdn.net/Master_Cui/article/details/109011218
else if (c->connectionType == Qt::BlockingQueuedConnection) {
if (receiverInSameThread) {
qWarning("Qt: Dead lock detected while activating a BlockingQueuedConnection: "
"Sender is %s(%p), receiver is %s(%p)",
sender->metaObject()->className(), sender,
receiver->metaObject()->className(), receiver);
}
QSemaphore semaphore;
{
QBasicMutexLocker locker(signalSlotLock(sender));
if (!c->receiver.loadAcquire())
continue;
QMetaCallEvent *ev = c->isSlotObject ?
new QMetaCallEvent(c->slotObj, sender, signal_index, argv, &semaphore) :
new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction,
sender, signal_index, argv, &semaphore);
QCoreApplication::postEvent(receiver, ev);
}
semaphore.acquire();
continue;
#endif
}
而当信号槽的连接方式是BlockingQueuedConnection时,直接通过事件循环机制将MetaCall事件放入循环队列中发送。只不过用信号量保护起来
上述代码的信号量的资源数为0,当信号槽都处于同一个线程时,信号先调用,执行到BlockingQueuedConnection分支时,QCoreApplication::postEvent(receiver, ev);调用结束后立刻返回,接着会执行semaphore.acquire();,acquire的默认实参是1,所以,此时线程会阻塞,又因为槽函数也处在当前线程(的事件队列)中,所以,槽函数也没法执行,而该信号量还没有release操作,所以,当信号槽处于同一线程且连接方式是BlockingQueuedConnection,会造成死锁。
而当信号和槽函数处于两个不同的线程时,虽然semaphore.acquire()会导致信号所在的线程阻塞,但不会导致槽函数所在的线程阻塞,当QMetaCallEvent被事件处理程序处理后,会立刻被删除,释放内存,而QMetaCallEvent的父类是QAbstractMetaCallEvent(是个纯虚基类),QAbstractMetaCallEvent的析构函数如下:
QAbstractMetaCallEvent::~QAbstractMetaCallEvent()
{
#if QT_CONFIG(thread)
if (semaphore_)
semaphore_->release();
#endif
}
用C++的多态机制,当QMetaCallEvent在槽函数的线程被销毁时,会调用QAbstractMetaCallEvent的析构函数,在析构函数中调用semaphore_->release();将信号量的资源释放,从而使得信号函数所在的线程的阻塞状态解除
所以,当信号槽的连接方式是BlockingQueuedConnection,如果信号槽处于同一线程,那么因为信号量的原因,会造成程序的死锁,但是如果信号槽处于不同线程,那么当槽函数所在的线程将会将信号量的资源释放掉,所以信号线程会先因为信号量阻塞,等待槽函数执行结束后将信号量的资源释放,接着信号线程获得信号量,然后继续执行