How Qt Signals and Slots Work - Qt的信号-槽机制是如何工作的

How Qt Signals and Slots Work - Qt的信号-槽机制是如何工作的


译:NewThinker_wei NewThinker_Jiwey @CSDN

(译注:这是原文和译文的对照版,不方便阅读,仅供对比参考。想看译文请点这里: http://blog.csdn.net/newthinker_wei/article/details/22785763 )


Qt is well known for its signals and slots mechanism. But how does it work? In this blog post, we will explore the internals of QObject and QMetaObject and discover how signals and slot work under the hood.

In this blog article, I show portions of Qt5 code, sometimes edited for formatting and brevity.

很多人都知道Qt的信号和槽机制,但它究竟是如何工作的呢?在本篇博文中,我们会探索QObject 和 QMetaObject 内部的一些具体实现,揭开信号-槽的神秘面纱。

在本篇博文中,我会贴出部分Qt5的源码,有时候还会根据需要改变一下格式并做一些简化。

First, let us recall how signals and slots look like by showing the official example.

The header looks like this:

首先,我们看一个官方例程,借此回顾一下信号和槽的使用方法。

头文件是这样的:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. class Counter : public QObject  
  2. {  
  3.     Q_OBJECT  
  4.     int m_value;  
  5. public:  
  6.     int value() const { return m_value; }  
  7. public slots:  
  8.     void setValue(int value);  
  9. signals:  
  10.     void valueChanged(int newValue);  
  11. };  


Somewhere in the .cpp file, we implementsetValue()

在源文件的某个地方,可以找到setValue()函数的实现:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void Counter::setValue(int value)  
  2. {  
  3.     if (value != m_value) {  
  4.         m_value = value;  
  5.         emit valueChanged(value);  
  6.     }  
  7. }  

Then one can use this Counter object like this:

然后编程者可以像下面这样使用Counter的对象:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Counter a, b;  
  2. QObject::connect(&a, SIGNAL(valueChanged(int)),  
  3.                  &b, SLOT(setValue(int)));  
  4.   
  5. a.setValue(12);  // a.value() == 12, b.value() == 12  

This is the original syntax that has almost not changed since the beginning of Qt in 1992.

But even if the basic API has not changed since the beginning, its implementation has been changed several times. New features have been added and a lot happened under the hood. There is no magic involved and this blog post will show you how it works.

这是从1992年Qt最初阶段开始就沿用下来而几乎没有变过的原始语法。

虽然基本的API并没有怎么变过,但它的实现方式却变了几次。很多新特性被添加了进来,底层的实现也发生了很多变化。不过这里面并没有什么神奇的难以理解的东西,本篇博文会向你展示这究竟是如何工作的,


MOC, the Meta Object Compiler - MOC,元对象编译器

The Qt signals/slots and property system are based on the ability to introspect the objects at runtime. Introspection means being able to list the methods and properties of an object and have all kinds of information about them such as the type of their arguments.
QtScript and QML would have hardly been possible without that ability.

Qt的信号/槽和属性系统都基于其能在运行时刻对对象进行实时考察的功能。实时考察意味着即使在运行过程中也可以列出一个对象有哪些方法(成员函数)和属性,以及关于它们的各种信息(比如参数类型)。如果没有实时考察这个功能,QtScript 和 QML 就基本不可能实现了。

C++ does not offer introspection support natively, so Qt comes with a tool to provide it. That tool is MOC. It is acode generator (and NOT a preprocessor like some people call it).

It parses the header files and generates an additional C++ file that is compiled with the rest of the program. That generated C++ file contains all the information required for the introspection.

Qt has sometimes been criticized by language purists because of this extra code generator. I will let theQt documentation respond to this criticism. There is nothing wrong with code generators and the MOC is of a great help.

C++本身不提供对实时考察的支持,所以Qt就推出了一个工具来提供这个支持。这个工具就是MOC。注意,它是一个代码生成器,而不是很多人说的“预编译器”。

MOC会解析头文件并为每一个含有Q_OBJECT宏的头文件生成一个对应的C++文件(这个文件会跟工程中的其他代码一块参与编译)。这个生成的C++文件包含了实时考察功能所需的全部信息(文件一般被命名为moc_HeaderName。cpp)。

因为这个额外的代码生成器,Qt有时对语言会有很严格的要求。 这里我就让这篇Qt文档来解释这个严格的要求。代码生成器没有什么错误,MOC起到了很大的作用。

Magic Macros-几个神奇的宏

Can you spot the keywords that are not pure C++ keywords? signals, slots, Q_OBJECT, emit, SIGNAL, SLOT. Those are known as the Qt extension to C++. They are in fact simple macros, defined inqobjectdefs.h

你能看出这几个关键字并不是标准C++的关键字吗?signals, slots, Q_OBJECT, emit, SIGNAL, SLOT. 这些都是Qt对C++的扩展。这几个关键字其实都是很简单的宏定义而已,在qobjectdefs.h 头文件中可以找到他们的定义。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #define signals public  
  2. #define slots /* nothing */  

That is right, signals and slots are simple functions: the compiler will handle them them like any other functions. The macros still serve a purpose though: theMOC will see them.

Signals were protected in Qt4 and before. They are becoming public in Qt5 in order to enablethe new syntax.

没错,信号和槽都是很简单的功能:编译器会将他们与其他任何宏一样处理。不过这些宏还有一个特殊的作用:MOC会发现他们。

Signals在Qt4之前都是protected类型的,他们在Qt5中变为了public,这样就可以使用一些新的语法了。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. #define Q_OBJECT \  
  2. public: \  
  3.     static const QMetaObject staticMetaObject; \  
  4.     virtual const QMetaObject *metaObject() const; \  
  5.     virtual void *qt_metacast(const char *); \  
  6.     virtual int qt_metacall(QMetaObject::Call, intvoid **); \  
  7.     QT_TR_FUNCTIONS /* translations helper */ \  
  8. private: \  
  9.     Q_DECL_HIDDEN static void qt_static_metacall(QObject *, QMetaObject::Call, intvoid **);  
  10.   
  11.   
  12. Q_OBJECT defines a bunch of functions and a static QMetaObject Those functions are implemented in the file generated by MOC.  
  13. #define emit /* nothing */  

emit is an empty macro. It is not even parsed by MOC. In other words, emit is just optional and means nothing (except being a hint to the developer).

emit是个空的宏定义,而且MOC也不会对它进行解析。也就是,emit完全是可有可无的,他没有任何意义(除了对开发者有提示作用)。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. Q_CORE_EXPORT const char *qFlagLocation(const char *method);  
  2. #ifndef QT_NO_DEBUG  
  3. # define QLOCATION "\0" __FILE__ ":" QTOSTRING(__LINE__)  
  4. # define SLOT(a)     qFlagLocation("1"#a QLOCATION)  
  5. # define SIGNAL(a)   qFlagLocation("2"#a QLOCATION)  
  6. #else  
  7. # define SLOT(a)     "1"#a  
  8. # define SIGNAL(a)   "2"#a  
  9. #endif  


(译注:对于#define的一些高级用法,参见我整理的一片文章:http://blog.csdn.net/newthinker_wei/article/details/8893407

Those macros just use the preprocessor to convert the parameter into a string, and add a code in front.

In debug mode we also annotate the string with the file location for a warning message if the signal connection did not work. This was added in Qt 4.5 in a compatible way. In order to know which strings have the line information, we useqFlagLocation which will register the string address in a table with two entries.

上面这些宏,会利用预编译器将一些参数转化成字符串,并且在前面添加上编码。

在调试模式中,如果singnal的连接出现问题,我们提示警告信息的时候还会注明对应的文件位置。这是在Qt4.5之后以兼容方式添加进来的功能。为了知道代码对应的行信息,我们可以用qFlagLocation ,它会将对应代码的地址信息注册到一个有两个入口的表里。

MOC Generated Code - MOC生成的代码

We will now go over portion of the code generated by moc in Qt5.

我们现在就来看看Qt5的moc生成的部分代码。

The QMetaObject

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. const QMetaObject Counter::staticMetaObject = {  
  2.     { &QObject::staticMetaObject, qt_meta_stringdata_Counter.data,  
  3.       qt_meta_data_Counter,  qt_static_metacall, 0, 0}  
  4. };  
  5.   
  6.   
  7. const QMetaObject *Counter::metaObject() const  
  8. {  
  9.     return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;  
  10. }  

We see here the implementation of Counter::metaObject() andCounter::staticMetaObject. They are declared in theQ_OBJECT macro.QObject::d_ptr->metaObject is only used for dynamic meta objects (QML Objects), so in general, the virtual functionmetaObject() just returns thestaticMetaObject of the class.

我们在这里可以看到Counter::metaObject()和 Counter::staticMetaObject 的实现。他们都是在Q_OBJECT宏中被声明的。

QObject::d_ptr->metaObject 只被用于动态元对象(QML对象),随意通常虚函数metaObject() 只是返回类的staticMetaObject 。

The staticMetaObject is constructed in the read-only data. QMetaObject as defined inqobjectdefs.h looks like this:

staticMetaObject被创建为只读数据。qobjectdefs.h 文件中QMetaObject的定义如下:

 

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. struct QMetaObject  
  2. {  
  3.     /* ... Skiped all the public functions ... */  
  4.   
  5.     enum Call { InvokeMetaMethod, ReadProperty, WriteProperty, /*...*/ };  
  6.   
  7.     struct { // private data  
  8.         const QMetaObject *superdata;  
  9.         const QByteArrayData *stringdata;  
  10.         const uint *data;  
  11.         typedef void (*StaticMetacallFunction)(QObject *, QMetaObject::Call, intvoid **);  
  12.         StaticMetacallFunction static_metacall;  
  13.         const QMetaObject **relatedMetaObjects;  
  14.         void *extradata; //reserved for future use  
  15.     } d;  
  16. };  

The d indirection is there to symbolize that all the member should be private. They are not private in order to keep it a POD and allow static initialization.

The QMetaObject is initialized with the meta object of the parent object (QObject::staticMetaObject in this case) assuperdata.stringdata anddata are initialized with some data explained later in this article.static_metacall is a function pointer initialized toCounter::qt_static_metacall.

代码中用的d是为了表明那些数据都本应为私有的。然而他们并没有成为私有的是为了保持它为POD和允许静态初始化。(译注:在C++中,我们把传统的C风格的struct叫做POD(Plain Old Data),字面意思古老的普通的结构体)。

这里会用父类对象的元对象(此处就是指QObject::staticMetaObject )来初始化QMetaObject的superdata,而它的stringdata 和 data 这两个成员则会用之后要讲到的数据初始化。static_metacall是一个被初始化为 Counter::qt_static_metacall的函数指针。

Introspection Tables - 实时考察功能用到的数据表

First, let us analyze the integer data of QMetaObject.

首先,我们来分析 QMetaObject的这个整型数组:

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. static const uint qt_meta_data_Counter[] = {  
  2.   
  3.  // content:  
  4.        7,       // revision  
  5.        0,       // classname  
  6.        0,    0, // classinfo  
  7.        2,   14, // methods  
  8.        0,    0, // properties  
  9.        0,    0, // enums/sets  
  10.        0,    0, // constructors  
  11.        0,       // flags  
  12.        1,       // signalCount  
  13.   
  14.  // signals: name, argc, parameters, tag, flags  
  15.        1,    1,   24,    2, 0x05,  
  16.   
  17.  // slots: name, argc, parameters, tag, flags  
  18.        4,    1,   27,    2, 0x0a,  
  19.   
  20.  // signals: parameters  
  21.     QMetaType::Void, QMetaType::Int,    3,  
  22.   
  23.  // slots: parameters  
  24.     QMetaType::Void, QMetaType::Int,    5,  
  25.   
  26.        0        // eod  
  27. };  

The first 13 int consists of the header. When there are two columns, the first column is the count and the second column is the index in this array where the description starts.
In this case we have 2 methods, and the methods description starts at index 14.

The method descriptions are composed of 5 int. The first one is the name, it is an index in the string table (we will look into the details later). The second integer is the number of parameters, followed by the index at which one can find the parameter description. We will ignore the tag and flags for now. For each function,moc also saves the return type of each parameter, their type and index to the name.

开头的13个整型数组成了结构体的头信息。对于有两列的那些数据,第一列表示某一类项目的个数,第二列表示这一类项目的描述信息开始于这个数组中的哪个位置(索引值)。

这里,我们的Counter类有两个方法,并且关于方法的描述信息开始于第14个int数据。

每个方法的描述信息由5个int型数据组成。第一个整型数代表方法名,它的值是该方法名(译注:方法名就是个字符串)在字符串表中的索引位置(之后会介绍字符串表)。第二个整数表示该方法所需参数的个数,后面紧跟的第三个数就是关于参数的描述(译注:它表示与参数相关的描述信息开始于本数组中的哪个位置,也是个索引)。我们现在先忽略掉tag和flags。对于每个函数,Moc还会保存它的返回类型、每个参数的类型、以及参数的名称。


String Table - 字符串表

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. struct qt_meta_stringdata_Counter_t {  
  2.     QByteArrayData data[6];  
  3.     char stringdata[47];  
  4. };  
  5. #define QT_MOC_LITERAL(idx, ofs, len) \  
  6.     Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \  
  7.     offsetof(qt_meta_stringdata_Counter_t, stringdata) + ofs \  
  8.         - idx * sizeof(QByteArrayData) \  
  9.     )  
  10. static const qt_meta_stringdata_Counter_t qt_meta_stringdata_Counter = {  
  11.     {  
  12. QT_MOC_LITERAL(0, 0, 7),  
  13. QT_MOC_LITERAL(1, 8, 12),  
  14. QT_MOC_LITERAL(2, 21, 0),  
  15. QT_MOC_LITERAL(3, 22, 8),  
  16. QT_MOC_LITERAL(4, 31, 8),  
  17. QT_MOC_LITERAL(5, 40, 5)  
  18.     },  
  19.     "Counter\0valueChanged\0\0newValue\0setValue\0"  
  20.     "value\0"  
  21. };  
  22. #undef QT_MOC_LITERAL  

This is basically a static array of QByteArray. theQT_MOC_LITERAL macro creates a staticQByteArray that references a particular index in the string below.

这主要就是一个QByteArray的静态数组。QT_MOC_LITERAL 这个宏可以创建一个静态的QByteArray ,其数据就是参考的在它下面的对应索引处的字符串。

Signals - 信号

The MOC also implements the signals. They are simple functions that just create an array of pointers to the arguments and pass that to QMetaObject::activate. The first element of the array is the return value. In our example it is 0 because the return value is void.
The 3rd parameter passed to activate is the signal index (0 in that case).
MOC还实现了信号signals(译注:根据前面的介绍我们已经知道signals其实就是publib,因此所有被定义的信号其实都必须有具体的函数定义,这样才能C++编译器编译,而我们在开发中并不曾写过信号的定义,这是因为这些都由MOC来完成)。所有的信号都是很简单的函数而已,他们只是为参数创建一个指针数组并传递给 QMetaObject::activate函数。指针数组的第一个元素是属于返回值的。在我们的例子中将它设置为了0,这是因为我们的返回类型是void。
传递给activate 函数的第3个参数是信号的索引(在这里,该索引为0)。
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. // SIGNAL 0  
  2. void Counter::valueChanged(int _t1)  
  3. {  
  4.     void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };  
  5.     QMetaObject::activate(this, &staticMetaObject, 0, _a);  
  6. }  
Calling a Slot - 调用槽
It is also possible to call a slot by its index in the  qt_static_metacall function:
通过一个槽的索引来调用一个槽函数也是行得通的,这要借助 qt_static_metacall这个函数:
[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void Counter::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)  
  2. {  
  3.     if (_c == QMetaObject::InvokeMetaMethod) {  
  4.         Counter *_t = static_cast<Counter *>(_o);  
  5.         switch (_id) {  
  6.         case 0: _t->valueChanged((*reinterpret_castint(*)>(_a[1]))); break;  
  7.         case 1: _t->setValue((*reinterpret_castint(*)>(_a[1]))); break;  
  8.         default: ;  
  9.         }  

The array pointers to the argument is the same format as the one used for the signal._a[0] is not touched because everything here returns void.

函数中的指针数组与在上面介绍signal时的那个函数中的指针数组格式相同。只不过这里没有用到_a[0],因为这里所有的函数都是返回void。

A Note About Indexes - 关于索引需要注意的地方
In each QMetaObject, the slots, signals and other invokable methods of that object are given an index, starting from 0. They are ordered so that the signals come first, then the slots and then the other methods. This index is called internally the  relative index. They do not include the indexes of the parents.

But in general, we do not want to know a more global index that is not relative to a particular class, but include all the other methods in the inheritance chain. To that, we just add an offset to that relative index and get theabsolute index. It is the index used in the public API, returned by functions likeQMetaObject::indexOf{Signal,Slot,Method}

The connection mechanism uses a vector indexed by signals. But all the slots waste space in the vector and there are usually more slots than signals in an object. So from Qt 4.6, a new internalsignal index which only includes the signal index is used.

While developing with Qt, you only need to know about the absolute method index. But while browsing the Qt's QObject source code, you must be aware of the difference between those three.

在每个 QMetaObject中,对象的槽、信号和其他一些被涉及到的方法都被分配了一个索引,这些索引值从0开始。他们是按照“先信号、再槽、最后其他方法”的顺序排列。这样得到的索引被称为相对索引( relative index),相对索引与该类的父类(就是基类)和祖宗类中的方法数量无关。
但是通常,我们不是想知道相对索引,而是想知道在包含了从父类和祖宗类中继承来的所有方法后的绝对索引。为了得到这个索引,我们只需要在相关索引( relative index)上加上一个偏移量就可以得到绝对索引 absolute index了。这个绝对索引就是在Qt的API中使用的, 像 QMetaObject::indexOf{Signal,Slot,Method}这样的函数返回的就是绝对索引。
另外,在信号槽的连接机制中还要用到一个关于信号的向量索引。这样的索引表中如果把槽也包含进来的话槽会造成向量的浪费,而一般槽的数量又要比信号多。所以从Qt4.6开始,Qt内部又多出了一个专门的信号索引signal index ,它是一个只包含了信号的索引表。
在用Qt开发的时候,我们只需要关心绝对索引就行。不过在浏览Qt源码的时候,要留意这三种索引的不同。
How Connecting Works - 连接是如何进行的

The first thing Qt does when doing a connection is to find out the index of the signal and the slot. Qt will look up in the string tables of the meta object to find the corresponding indexes.

Then a QObjectPrivate::Connection object is created and added in the internal linked lists.

What information needs to be stored for each connection? We need a way to quickly access the connections for a given signal index. Since there can be several slots connected to the same signal, we need for each signal to have a list of the connected slots. Each connection must contain the receiver object, and the index of the slot. We also want the connections to be automatically destroyed when the receiver is destroyed, so each receiver object needs to know who is connected to him so he can clear the connection.

Here is the QObjectPrivate::Connection as defined in qobject_p.h :

在进行信号和槽的连接时Qt做的一件事就是找出要连接的信号和槽的索引。Qt会在meta object的字符串表中查找对应的索引。

然后会创建一个QObjectPrivate::Connection对象并添加到内部的链表中。

一个 connection 中需要存储哪些数据呢?我们需要一种能根据信号索引signal index快速访问到对应的connection的方法。因为可能会同时有不止一个槽连接到同一个信号上,所以每一个信号都要有一个槽列表;每个connection必须包含接收对象的指针以及被连接的槽的索引;我们还想让一个connection能在与之对应的接收者被销毁时自动取消连接,所以每个接收者对象也需要知道谁与他建立了连接这样才能在析构时将connection清理掉。

下面是定义在 qobject_p.h 中的QObjectPrivate::Connection :

(译注:只认识QObject不认识QObjectPrivate?看这里:http://blog.csdn.net/newthinker_wei/article/details/22789885)

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. struct QObjectPrivate::Connection  
  2. {  
  3.     QObject *sender;  
  4.     QObject *receiver;  
  5.     union {  
  6.         StaticMetaCallFunction callFunction;  
  7.         QtPrivate::QSlotObjectBase *slotObj;  
  8.     };  
  9.     // The next pointer for the singly-linked ConnectionList  
  10.     Connection *nextConnectionList;  
  11.     //senders linked list  
  12.     Connection *next;  
  13.     Connection **prev;  
  14.     QAtomicPointer<const int> argumentTypes;  
  15.     QAtomicInt ref_;  
  16.     ushort method_offset;  
  17.     ushort method_relative;  
  18.     uint signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())  
  19.     ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking  
  20.     ushort isSlotObject : 1;  
  21.     ushort ownArgumentTypes : 1;  
  22.     Connection() : nextConnectionList(0), ref_(2), ownArgumentTypes(true) {  
  23.         //ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection  
  24.     }  
  25.     ~Connection();  
  26.     int method() const { return method_offset + method_relative; }  
  27.     void ref() { ref_.ref(); }  
  28.     void deref() {  
  29.         if (!ref_.deref()) {  
  30.             Q_ASSERT(!receiver);  
  31.             delete this;  
  32.         }  
  33.     }  
  34. };  


Each object has then a connection vector: It is a vector which associates for each of the signals a linked lists ofQObjectPrivate::Connection.
Each object also has a reversed lists of connections the object is connected to for automatic deletion. It is a doubly linked list.

每一个对象有一个connection vector:每一个信号有一个 QObjectPrivate::Connection的链表,这个vector就是与这些链表相关联的。
每一个对象还有一个反向链表,它包含了这个对象被连接到的所有的 connection,这样可以实现连接的自动清除。而且这个反向链表是一个双重链表。



Linked lists are used because they allow to quickly add and remove objects. They are implemented by having the pointers to the next/previous nodes withinQObjectPrivate::Connection
Note that the prev pointer of thesenderList is a pointer to a pointer. That is because we don't really point to the previous node, but rather to the pointer to the next in the previous node. This pointer is only used when the connection is destroyed, and not to iterate backwards. It allows not to have a special case for the first item.

使用链表是因为它们可以快速地添加和移除对象,它们靠保存在QObjectPrivate::Connection中的next/previous前后节点的指针来实现这些操作。

注意senderListprev指针是一个指针的指针。这是因为我们不是真的要指向前一个节点,而是要指向一个指向前节点的指针。这个指针的指针只有在销毁连接时才用到,而且不要用它重复往回迭代。这样设计可以不用对链表的首结点做特殊处理。

(译注:对连接的建立如果还有疑惑,请参考:http://blog.csdn.net/newthinker_wei/article/details/22791617



Signal Emission - 信号的发送

When we call a signal, we have seen that it calls the MOC generated code which callsQMetaObject::activate.

Here is an annotated version of its implementation from qobject.cpp

我们已经知道当调用一个信号的时候,最终调用的是MOC生成的代码中的QMetaObject::activate函数。

这里是qobject.cpp中这个函数的实现代码,这里贴出来的只是一个注解版本。

[cpp]  view plain copy 在CODE上查看代码片 派生到我的代码片
  1. void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,  
  2.                            void **argv)  
  3. {  
  4.     activate(sender, QMetaObjectPrivate::signalOffset(m), local_signal_index, argv);  
  5.     /* We just forward to the next function here. We pass the signal offset of 
  6.      * the meta object rather than the QMetaObject itself 
  7.      * It is split into two functions because QML internals will call the later. */  
  8. }  
  9.   
  10. void QMetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)  
  11. {  
  12.     int signal_index = signalOffset + local_signal_index;  
  13.   
  14.     /* The first thing we do is quickly check a bit-mask of 64 bits. If it is 0, 
  15.      * we are sure there is nothing connected to this signal, and we can return 
  16.      * quickly, which means emitting a signal connected to no slot is extremely 
  17.      * fast. */  
  18.     if (!sender->d_func()->isSignalConnected(signal_index))  
  19.         return// nothing connected to these signals, and no spy  
  20.   
  21.     /* ... Skipped some debugging and QML hooks, and some sanity check ... */  
  22.   
  23.     /* We lock a mutex because all operations in the connectionLists are thread safe */  
  24.     QMutexLocker locker(signalSlotLock(sender));  
  25.   
  26.     /* Get the ConnectionList for this signal.  I simplified a bit here. The real code 
  27.      * also refcount the list and do sanity checks */  
  28.     QObjectConnectionListVector *connectionLists = sender->d_func()->connectionLists;  
  29.     const QObjectPrivate::ConnectionList *list =  
  30.         &connectionLists->at(signal_index);  
  31.   
  32.     QObjectPrivate::Connection *c = list->first;  
  33.     if (!c) continue;  
  34.     // We need to check against last here to ensure that signals added  
  35.     // during the signal emission are not emitted in this emission.  
  36.     QObjectPrivate::Connection *last = list->last;  
  37.   
  38.     /* Now iterates, for each slot */  
  39.     do {  
  40.         if (!c->receiver)  
  41.             continue;  
  42.   
  43.         QObject * const receiver = c->receiver;  
  44.         const bool receiverInSameThread = QThread::currentThreadId() == receiver->d_func()->threadData->threadId;  
  45. //译注:注意下面这一段,从这里可以看出对于跨线程的连接,信号发出  
  46. //后槽函数不会立即在当前线程中执行。其执行要等到槽函数所在的线程被  
  47. //激活后。有时间了再研究一下queued_activate这个函数。  
  48.         // determine if this connection should be sent immediately or  
  49.         // put into the event queue  
  50.         if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)  
  51.             || (c->connectionType == Qt::QueuedConnection)) {  
  52.             /* Will basically copy the argument and post an event */  
  53.             queued_activate(sender, signal_index, c, argv);  
  54.             continue;  
  55.         } else if (c->connectionType == Qt::BlockingQueuedConnection) {  
  56.             /* ... Skipped ... */  
  57.             continue;  
  58.         }  
  59.   
  60.         /* Helper struct that sets the sender() (and reset it backs when it 
  61.          * goes out of scope */  
  62.         QConnectionSenderSwitcher sw;  
  63.         if (receiverInSameThread)  
  64.             sw.switchSender(receiver, sender, signal_index);  
  65.   
  66.         const QObjectPrivate::StaticMetaCallFunction callFunction = c->callFunction;  
  67.         const int method_relative = c->method_relative;  
  68.         if (c->isSlotObject) {  
  69.             /* ... Skipped....  Qt5-style connection to function pointer */  
  70.         } else if (callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {  
  71.             /* If we have a callFunction (a pointer to the qt_static_metacall 
  72.              * generated by moc) we will call it. We also need to check the 
  73.              * saved metodOffset is still valid (we could be called from the 
  74.              * destructor) */  
  75.             locker.unlock(); // We must not keep the lock while calling use code  
  76.             callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv);  
  77.             locker.relock();  
  78.         } else {  
  79.             /* Fallback for dynamic objects */  
  80.             const int method = method_relative + c->method_offset;  
  81.             locker.unlock();  
  82.             metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv);  
  83.             locker.relock();  
  84.         }  
  85.   
  86.         // Check if the object was not deleted by the slot  
  87.         if (connectionLists->orphaned) break;  
  88.     } while (c != last && (c = c->nextConnectionList) != 0);  
  89. }  





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值