前面已经分析了元对象系统、MOC文件和信号槽的连接,本文分析下信号槽的时序
信号的触发通过emit关键字触发,以sigf1为例,通常是这样的
emit sigf1(t1)
emit就是个空宏,在qobjectdefs.h中,没啥用,实质就是个函数调用
#ifndef QT_NO_EMIT
# define emit
#endif
而函数sigf1在MOC中实现
void moctest::sigf1(double _t1)
{
void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(std::addressof(_t1))) };
QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
定义数组后,调用QMetaObject::activate(在qobject.cpp中)
void QMetaObject::activate(QObject *sender, const QMetaObject *m, int local_signal_index,
void **argv)
{
int signal_index = local_signal_index + QMetaObjectPrivate::signalOffset(m);//计算信号的绝对序号
if (Q_UNLIKELY(qt_signal_spy_callback_set.loadRelaxed()))//如果为true,表示执行qt的测试代码,执行那个分支无所谓,与qt_signal_spy_callback_set相关的都不重要
doActivate<true>(sender, signal_index, argv);
else
doActivate<false>(sender, signal_index, argv);
}
loadrelaxed调用的其实就是C++11中的atomic类中的load方法,返回的是其中类或结构中包含的数据详细见http://www.cplusplus.com/reference/atomic/atomic/load/
template <bool callbacks_enabled>
void doActivate(QObject *sender, int signal_index, void **argv)
{
QObjectPrivate *sp = QObjectPrivate::get(sender);//获取信号的发射对象的QObject数据
if (sp->blockSig)//如果信号阻塞标志位为true,不调用槽函数
return;
Q_TRACE_SCOPE(QMetaObject_activate, sender, signal_index);
if (sp->isDeclarativeSignalConnected(signal_index)//qml相关,暂时不用管
&& QAbstractDeclarativeData::signalEmitted) {
Q_TRACE_SCOPE(QMetaObject_activate_declarative_signal, sender, signal_index);
QAbstractDeclarativeData::signalEmitted(sp->declarativeData, sender,
signal_index, argv);
}
const QSignalSpyCallbackSet *signal_spy_set = callbacks_enabled ? qt_signal_spy_callback_set.loadAcquire() : nullptr;
void *empty_argv[] = { nullptr };
if (!argv)
argv = empty_argv;
if (!sp->maybeSignalConnected(signal_index)) {//可能是声明性关联,如果是,啥也不做,没太懂,但是问题不大
// The possible declarative connection is done, and nothing else is connected
if (callbacks_enabled && signal_spy_set->signal_begin_callback != nullptr)
signal_spy_set->signal_begin_callback(sender, signal_index, argv);
if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr)
signal_spy_set->signal_end_callback(sender, signal_index);
return;
}
if (callbacks_enabled && signal_spy_set->signal_begin_callback != nullptr)//不执行
signal_spy_set->signal_begin_callback(sender, signal_index, argv);
bool senderDeleted = false;
{
Q_ASSERT(sp->connections.loadAcquire());
QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadRelaxed());//发送对象的所有连接信息
QObjectPrivate::SignalVector *signalVector = connections->signalVector.loadRelaxed();//获取指向信号connection list的指针
const QObjectPrivate::ConnectionList *list;
if (signal_index < signalVector->count())
list = &signalVector->at(signal_index);//根据绝对序号获取对应信号的ConnectionList
else
list = &signalVector->at(-1);//如果信号的绝对序号不合法,获取第一个信号的ConnectionList
Qt::HANDLE currentThreadId = QThread::currentThreadId();//获取当前程序的线程ID
bool inSenderThread = currentThreadId == QObjectPrivate::get(sender)->threadData->threadId.loadRelaxed();//判断发送端和当前程序的是否在同一线程
// We need to check against the highest connection id to ensure that signals added
// during the signal emission are not emitted in this emission.
uint highestConnectionId = connections->currentConnectionId.loadRelaxed();
do {
QObjectPrivate::Connection *c = list->first.loadRelaxed();//找到connectionlist中的第一个connection
if (!c)
continue;
do {
QObject * const receiver = c->receiver.loadRelaxed();//找到connection中的接收信号的对象
if (!receiver)
continue;
QThreadData *td = c->receiverThreadData.loadRelaxed();//接收信号对象程序所在的线程数据
if (!td)
continue;
bool receiverInSameThread;
if (inSenderThread) {//多线程处理
receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();
} else {
// need to lock before reading the threadId, because moveToThread() could interfere
QMutexLocker lock(signalSlotLock(receiver));
receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();
}
// determine if this connection should be sent immediately or
// put into the event queue
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
|| (c->connectionType == Qt::QueuedConnection)) {//接收端和发送端在多线程的情况下,调用queued_activate
queued_activate(sender, signal_index, c, argv);
continue;
#if QT_CONFIG(thread)
} else if (c->connectionType == Qt::BlockingQueuedConnection) {//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
}
QObjectPrivate::Sender senderData(receiverInSameThread ? receiver : nullptr, sender, signal_index);//根据发送端和接收端是否在同一线程对senderData进行初始化
if (c->isSlotObject) {//如果是Qt5中的新式语法,槽函数的对象不为空
c->slotObj->ref();
struct Deleter {
void operator()(QtPrivate::QSlotObjectBase *slot) const {
if (slot) slot->destroyIfLastRef();
}
};//自定义智能指针unique_ptr的删除器
const std::unique_ptr<QtPrivate::QSlotObjectBase, Deleter> obj{c->slotObj};
{
Q_TRACE_SCOPE(QMetaObject_activate_slot_functor, obj.get());
obj->call(receiver, argv);//直接调用槽函数
}
} else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {//如果是旧式语法,使用信号槽的宏,那么需要调用callFunction,
//we compare the vtable to make sure we are not in the destructor of the object.
const int method_relative = c->method_relative;//获得相对序号
const auto callFunction = c->callFunction;
const int methodIndex = (Q_HAS_TRACEPOINTS || callbacks_enabled) ? c->method() : 0;
if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr)
signal_spy_set->slot_begin_callback(receiver, methodIndex, argv);
{
Q_TRACE_SCOPE(QMetaObject_activate_slot, receiver, methodIndex);
callFunction(receiver, QMetaObject::InvokeMetaMethod, method_relative, argv);//callFunction就是MOC文件中的qt_static_metacall
}
if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
signal_spy_set->slot_end_callback(receiver, methodIndex);
} else {//还有一种旧式语法,没有用到,先不用管,问题不大
const int method = c->method_relative + c->method_offset;
if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr) {
signal_spy_set->slot_begin_callback(receiver, method, argv);
}
{
Q_TRACE_SCOPE(QMetaObject_activate_slot, receiver, method);
QMetaObject::metacall(receiver, QMetaObject::InvokeMetaMethod, method, argv);
}
if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
signal_spy_set->slot_end_callback(receiver, method);
}
} while ((c = c->nextConnectionList.loadRelaxed()) != nullptr && c->id <= highestConnectionId);//内层循环,遍历对应信号的连接连接列表
} while (list != &signalVector->at(-1) &&
//start over for all signals;
((list = &signalVector->at(-1)), true));//外层循环,遍历每个信号的connectionlist
if (connections->currentConnectionId.loadRelaxed() == 0)
senderDeleted = true;
}
if (!senderDeleted) {
sp->connections.loadRelaxed()->cleanOrphanedConnections(sender);
if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr)
signal_spy_set->signal_end_callback(sender, signal_index);
}
}
上面的代码看着很多,先不管多线程的情况,只考虑直连的情况,开始调用信号函数,然后调用activate,之后再调用doactivate,接着就是根据信号的绝对序号找到信号的connectionlist,遍历该connectionlist中的每个connection(connection通过connect函数得到并添加++),然后看每个connection中是否有函数指针qt_static_metacall或者有函数指针所在的类对象,然后依次调用qt_static_metacall或者直接调用成员函数
再次引用https://qtguide.ustclug.org/中的图就是这样的
参考
Qt5.14源码
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出