前言
前面讲了Qt module小模块介绍, 后续将会接着小模块讲解Qt核心机制相关,Qt核心主要涉及到信号槽, 属性机制, 事件模型, 元对象等。这篇博客主要核心功能机制–信号槽进行详解。
信号槽
信号槽源码分析 这篇博客还是比较详细, 可以参考。
众所周知, 信号槽是我们在Qt中常用的一种通信机制(同步, 异步都可)。
我们常常通过connect去连接信号槽, 具体形式可参考Qt connect几种写法与连接方式
我们都知道当我们声明一个直接或者间接继承自QObject类后,
class MyObject: public QObject
{
Q_OBJECT //声明
Q_SIGNALS://信号
void sigBtnClicked(int);
public Q_SLOTS://槽
}
先看一下 Q_OBJECT展开
/* qmake ignore Q_OBJECT */
#define Q_OBJECT \
public: \
Q_OBJECT_CHECK \
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, "")
先暂时知道这些函数, 然后编译器在编译后, 你会发现多生成了 moc_MyObject.cpp 文件. 稍微细心, 你会发现这有对Q_OBJECT展开的函数进行定义实现., 具体我并未写那么多, 把核心函数写下来.
QT_BEGIN_MOC_NAMESPACE
void MyObject::qt_static_metacall(QObject* _o, QMetaObject::call _c, int _id, void **a)
{
if (_c == QMetaObject::InvokeMetaMethod) {
Q_ASSERT(staticMetaObject.cast(_o));
MainWindow *_t = static_cast<MainWindow *>(_o);
Q_UNUSED(_t)
switch (_id) {
case 0: _t->sigBtnClicked((*reinterpret_cast< int(*)>(_a[1]))); break;
default: ;
}
}
else if (_c == QMetaObject::IndexOfMethod) {
int *result = reinterpret_cast<int *>(_a[0]);
void **func = reinterpret_cast<void **>(_a[1]);
{
typedef void (MainWindow::*_t)(int );
if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&MainWindow::sigBtnClicked)) {
*result = 0;
return;
}
}
}
}
...
// SIGNAL 0
void MyObject::sigBtnClicked(int _t1)
{
void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
QT_END_MOC_NAMESPACE
我们知道Qt中 信号是不用我们 实现的, 那是因为在上式文件中Qt在编译过程文件已经实现了. 当我们发送emit sigBtnClicked(); 时, 其实就是调用moc_MyObject.cpp 中sigBtnClicked() 这个函数.
接下来, 我们顺藤摸瓜, 看看 sigBtnClicked() 函数. 它里面调用了 QMetaObject::activate;
QMetaObject::activate
主要我们就会发现
触发信号 => QMetaObject::activate => 获取信号发送者connectionLists,找到信号对应的的ConnectionList 然后循环遍历该连接表,单个 Connection => 如果同一个线程直接调用接收者的qt_metaCall 最终函数回调调用了qt_static_metacall, 如果不是在同一线程, 会向接收者所在线程队列post QMetaCallEvent事件, 等接受者线程轮循到该事件时, 在处理,最后最终函数回调调用了qt_static_metacall。
如果是异步的话, 接收者与发送者不在同一个线程之内, queued_activate()
static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
{
if (!c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) {
QMetaMethod m = sender->metaObject()->method(signal);
int *tmp = queuedConnectionTypes(m.parameterTypes());
if (!tmp) // cannot queue arguments
tmp = &DIRECT_CONNECTION_ONLY;
if (!c->argumentTypes.testAndSetOrdered(0, tmp)) {
if (tmp != &DIRECT_CONNECTION_ONLY)
delete [] tmp;
}
}
if (c->argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate
return;
int nargs = 1; // include return type
while (c->argumentTypes[nargs-1])
++nargs;
int *types = (int *) qMalloc(nargs*sizeof(int));
Q_CHECK_PTR(types);
void **args = (void **) qMalloc(nargs*sizeof(void *));
Q_CHECK_PTR(args);
types[0] = 0; // return type
args[0] = 0; // return value
for (int n = 1; n < nargs; ++n)
args[n] = QMetaType::construct((types[n] = c->argumentTypes[n-1]), argv[n]);
QCoreApplication::postEvent(c->receiver, new QMetaCallEvent(c->method_offset,
c->method_relative,
c->callFunction,
sender, signal, nargs,
types, args));
}
Connections
上一节说到, 信号的发送方会有对应链接列表,
如图方便更好的理解连接列表。
每个ConnectionList类型元素是一个双向链表,保存了信号的所有连接。对于每一个连接的类型Connection结构如下:
struct Connection
{
QObject *sender;//发送者
QObject *receiver;//接受者
StaticMetaCallFunction callFunction;//调用的槽函数
// The next pointer for the singly-linked ConnectionList
Connection *nextConnectionList;
//senders linked list
Connection *next;
Connection **prev;
QBasicAtomicPointer<int> argumentTypes;
ushort method_offset;
ushort method_relative;
ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
~Connection();
int method() const { return method_offset + method_relative; }
};
里面包含了发送者, 接收者,下一个连接, 连接类型, 以及调用槽函数StaticMetaCallFunction callFunction等等。
而StaticMetaCallFunction 作为一个函数指针, 存在QObject对象的私有数据QObjectPrivate:
class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{
Q_DECLARE_PUBLIC(QObject)
public:
struct ExtraData
{
ExtraData() {}
QList<QByteArray> propertyNames;
QList<QVariant> propertyValues;
};
typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);
struct Connection
{
QObject *sender;
QObject *receiver;
StaticMetaCallFunction callFunction;
// The next pointer for the singly-linked ConnectionList
Connection *nextConnectionList;
//senders linked list
Connection *next;
Connection **prev;
QBasicAtomicPointer<int> argumentTypes;
ushort method_offset;
ushort method_relative;
ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
~Connection();
int method() const { return method_offset + method_relative; }
};
// 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 void clearGuards(QObject *);
static QObjectPrivate *get(QObject *o) {
return o->d_func();
}
int signalIndex(const char *signalName) const;
inline bool isSignalConnected(uint signalIdx) const;
// To allow arbitrary objects to call connectNotify()/disconnectNotify() without making
// the API public in QObject. This is used by QDeclarativeNotifierEndpoint.
inline void connectNotify(const char *signal);
inline void disconnectNotify(const char *signal);
static inline void signalSignature(const QMetaMethod &signal,
QVarLengthArray<char> *result);
public:
QString objectName;
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];
// preserve binary compatibility with code compiled without Qt 3 support
// keeping the binary layout stable helps the Qt Creator debugger
void *unused;
QList<QPointer<QObject> > eventFilters;
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<QtSharedPointer::ExternalRefCountData> sharedRefcount;
};
函数回调
typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **); //是qt_static_metacall函数指针
结果
不管所显示的 StaticMetaCallFunction callFunction, 以及qt_metaCall等最终还是会调用的 qt_static_metacall 去调用函数。
最后结果来说: 就是函数回调。 只不过可能是需要查询其信号所发送者connectionLists, connection处理花费一定性能。