目录
- 信号槽机制
- 定义
- 连接
- 一、标准connect链接
- 1、QObject::connect
- 2、QMetaObjectPrivate::connect
- 3、信号槽容器Conntion、ConnectionList、SignalVector、ConnectionData
- 二、函数指针链接(qt5后)
- 三、Lamba表达式
- 触发
信号槽机制
信号槽是观察者模式的一种实现,订阅-发布。一个信号就是一个能够被观察的事件,一个槽就是一个观察者,被观察者-多观察者。信号发出者通过遍历信号槽容器来通知所有连接的槽函数。
信号槽本质上是两个对象的函数地址映射单线程下,相当于函数指针调用;多线程下,发送者将槽函数的调用转化为一次调用事件放入事件循环队列,接收者线程执行到下一次事件处理时,调用相应的函数。
信号槽的效率比直接调用函数慢十倍
定义
在一个类中通过signals和slots
宏定义信号和槽函数,两个都是qobjectdefs.h中定义的宏。signals
最终被替换为public
,slots
最终被替换为空白,所以signals:即public:
,public slots:即public:
。也就是说从C++的角度来看,信号是一个public属性的成员函数,函数体由MOC生成,定义在moc文件中,而槽函数是一个对应属性的成员函数,函数体由自己定义。
#define slots Q_SLOTS
#define signals Q_SIGNALS
# define Q_SLOTS QT_ANNOTATE_ACCESS_SPECIFIER(qt_slot)
# define Q_SIGNALS public QT_ANNOTATE_ACCESS_SPECIFIER(qt_signal)
# define QT_ANNOTATE_ACCESS_SPECIFIER(x)
QDBusAdaptorConnector类中名为relaySignal
的信号,MOC编译器编译过后在moc_qdbusadaptorconnector.cpp中生成的信号函数体QDBusAdaptorConnector::relaySignal
,在函数内部调用QMetaObject::activate
来触发连接的槽函数。
// SIGNAL 0
void QDBusAdaptorConnector::relaySignal(QObject * _t1, const QMetaObject * _t2, int _t3, const QVariantList & _t4)
{
//----------_a是返回值和参数--------------------void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)), const_cast<void*>(reinterpret_cast<const void*>(&_t3)), const_cast<void*>(reinterpret_cast<const void*>(&_t4)) };QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
连接
QT使用connect()
函数连接信号槽,connect()
的最后一个参数将指定连接类型:
Qt::DirectConnection
:直接连接意味着槽函数将在信号发出的线程直接调用Qt::QueuedConnection
:队列连接意味着向接受者所在线程发送一个事件,该线程的事件循环将获得这个事件,然后之后的某个时刻调用槽函数Qt::BlockingQueuedConnection
:阻塞的队列连接就像队列连接,但是发送者线程将会阻塞,直到接受者所在线程的事件循环获得这个事件,槽函数被调用之后,函数才会返回Qt::AutoConnection
:自动连接(默认)意味着如果接受者所在线程就是当前线程,则使用直接连接;否则将使用队列连接
一、标准connect链接
connect(this, SIGNAL(signalOne(int)), this, SLOT(slotOne(int)));
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
这个参数类型的connect()
通过SIGNAL和SLOT
两个宏将信号和槽转换为字符串,qFlagLocation
是将字符串存储到QThreadData的flaggedSignatures
成员中用于定位,最后返回原字符串。
# define SLOT(a) qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a) qFlagLocation("2"#a QLOCATION)
Q_CORE_EXPORT const char *qFlagLocation(const char *method);
# define QLOCATION "\0" __FILE__ ":" QT_STRINGIFY(__LINE__)
const char *qFlagLocation(const char *method)
{QThreadData *currentThreadData = QThreadData::current(false);if (currentThreadData != nullptr)currentThreadData->flaggedSignatures.store(method);return method;
}
将信号槽的函数名称按照相应规则组合成字符串,通过connect
解析字符串,分别获取信号槽的绝对索引,然后将信号槽的链接关系添加到信号槽容器中。所以信号槽的检查都在运行时解析字符串的方式进行,即使拼写错误,也可以编译成功,只会在运行时报错。
1、QObject::connect
- 检查四个参数是否有空值,有则打印错误并返回。
check_signal_macro
检查signal
是否被成功转换为对应格式的字符串(第二个参数是否使用了SIGNAL
宏,而不是SLOT
或其它),判断signal的首字符是否为2,其它参数用来打印错误信息。- 获取signal的相对索引,加上偏移值得到绝对索引。
check_method_code
检查method
是否被成功转换为对应格式的字符串(第四个参数是否使用了SIGNAL
宏或SLOT
宏,而不是其它,第四个可以为SIGNAL
宏,即信号连接信号),判断signal
的首字符是否为1或2,其它参数用来打印错误信息。- 根据
method
是信号还是槽分别获取method
的相对索引。 - 检查参数是否匹配。
- 检查连接方式。
- 调用
QMetaObject::Connection
存储signal
和method
信息到信号槽容器。
QMetaObject::Connection QObject::connect(const QObject *sender, const char *signal,const QObject *receiver, const char *method,Qt::ConnectionType type)
{//检查四个参数是否有空if (sender == nullptr || receiver == nullptr || signal == nullptr || method == nullptr) {qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",sender ? sender->metaObject()->className() : "(nullptr)",(signal && *signal) ? signal+1 : "(nullptr)",receiver ? receiver->metaObject()->className() : "(nullptr)",(method && *method) ? method+1 : "(nullptr)");return QMetaObject::Connection(0);}QByteArray tmp_signal_name;
//验证信号宏指令(SIGNAL)的正确性if (!check_signal_macro(sender, signal, "connect", "bind"))return QMetaObject::Connection(0);const QMetaObject *smeta = sender->metaObject();const char *signal_arg = signal;++signal; //skip codeQArgumentTypeArray signalTypes;Q_ASSERT(QMetaObjectPrivate::get(smeta)->revision >= 7);QByteArray signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
//获取信号相对索引int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signalName, signalTypes.size(), signalTypes.constData());if (signal_index < 0) {// check for normalized signaturestmp_signal_name = QMetaObject::normalizedSignature(signal - 1);signal = tmp_signal_name.constData() + 1;signalTypes.clear();signalName = QMetaObjectPrivate::decodeMethodSignature(signal, signalTypes);smeta = sender->metaObject();signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signalName, signalTypes.size(), signalTypes.constData());}if (signal_index < 0) {err_method_notfound(sender, signal_arg, "connect");err_info_about_objects("connect", sender, receiver);return QMetaObject::Connection(0);}signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
//加上偏移量得到信号全局索引值(绝对索引)signal_index += QMetaObjectPrivate::signalOffset(smeta);QByteArray tmp_method_name;int membcode = extract_code(method);
//检查槽函数代码是否有效if (!check_method_code(membcode, receiver, method, "connect"))return QMetaObject::Connection(0);const char *method_arg = method;++method; // skip codeQArgumentTypeArray methodTypes;QByteArray methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);const QMetaObject *rmeta = receiver->metaObject();int method_index_relative = -1;Q_ASSERT(QMetaObjectPrivate::get(rmeta)->revision >= 7);
//获取槽函数(或信号,信号连接信号时)相对索引switch (membcode) {case QSLOT_CODE:method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, methodName, methodTypes.size(), methodTypes.constData());break;case QSIGNAL_CODE:method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, methodName, methodTypes.size(), methodTypes.constData());break;}if (method_index_relative < 0) {// check for normalized methodstmp_method_name = QMetaObject::normalizedSignature(method);method = tmp_method_name.constData();methodTypes.clear();methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);// rmeta may have been modified abovermeta = receiver->metaObject();switch (membcode) {case QSLOT_CODE:method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, methodName, methodTypes.size(), methodTypes.constData());break;case QSIGNAL_CODE:method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, methodName, methodTypes.size(), methodTypes.constData());break;}}if (method_index_relative < 0) {err_method_notfound(receiver, method_arg, "connect");err_info_about_objects("connect", sender, receiver);return QMetaObject::Connection(0);}
//检查连接信号与槽时的参数是否匹配。if (!QMetaObjectPrivate::checkConnectArgs(signalTypes.size(), signalTypes.constData(),methodTypes.size(), methodTypes.constData())) {qWarning("QObject::connect: Incompatible sender/receiver arguments""\n %s::%s --> %s::%s",sender->metaObject()->className(), signal,receiver->metaObject()->className(), method);return QMetaObject::Connection(0);}int *types = 0;
//检查连接方式if ((type == Qt::QueuedConnection)&& !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {return QMetaObject::Connection(0);}#ifndef QT_NO_DEBUGQMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());check_and_warn_compat(smeta, smethod, rmeta, rmethod);
#endif
//调用QMetaObjectPrivate::connectQMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));return handle;
}
2、QMetaObjectPrivate::connect
QMetaObjectPrivate::connect
将signal
和method
的信息存储到信号槽容器中,也就是将二者连接起来。
- 获取
method
的偏移值。 - 连接类型为
Qt::UniqueConnection
时检查是否已经连接,若是直接返回 - 创建
Connection
类c,填充数据(包括发送者、信号索引、接收者、method
相对索引和绝对索引、连接类型、参数类型、调用函数),一个connection
对象对应一个connect()
函数的连接,将该对象 - 存储到
connectionList
。 - 通知发送者,它的信号已经被连接到一个槽函数上。
QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,int signal_index, const QMetaObject *smeta,const QObject *receiver, int method_index,const QMetaObject *rmeta, int type, int *types)
{
//Const_cast 变成可写QObject *s = const_cast<QObject *>(sender);QObject *r = const_cast<QObject *>(receiver);int method_offset = rmeta ? rmeta->methodOffset() : 0;Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);QObjectPrivate::StaticMetaCallFunction callFunction = rmeta ? rmeta->d.static_metacall : nullptr;
//确保在多线程环境下,信号和槽的连接操作是线程安全的QOrderedMutexLocker locker(signalSlotLock(sender),signalSlotLock(receiver));
//type为Qt::UniqueConnection方式时排重QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed();if (type & Qt::UniqueConnection && scd) {if (scd->signalVectorCount() > signal_index) {//一个信号对应一个signalVector中的元素,是connectionlist类,所以一个信号对应一个connectionlistconst QObjectPrivate::Connection *c2 = scd->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();int method_index_absolute = method_index + method_offset;while (c2) {//已经存在就不建立连接if (!c2->isSlotObject && c2->receiver.loadRelaxed() == receiver && c2->method() == method_index_absolute)return nullptr;c2 = c2->nextConnectionList.loadRelaxed();}}type &= Qt::UniqueConnection - 1;}
//创建Connection类c,填充数据,一个connection对象对应一个connect连接std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};c->sender = s;c->signal_index = signal_index;c->receiver.storeRelaxed(r);QThreadData *td = r->d_func()->threadData;td->ref();c->receiverThreadData.storeRelaxed(td);c->method_relative = method_index;c->method_offset = method_offset;c->connectionType = type;c->isSlotObject = false;c->argumentTypes.storeRelaxed(types);c->callFunction = callFunction;
//将创建的connection存储到connectionLists(多个connection的qvector),get返回发送者的私有类QObjectPrivate::get(s)->addConnection(signal_index, c.get());locker.unlock();
//通知发送者,它的信号已经被连接到一个槽函数上QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);if (smethod.isValid())s->connectNotify(smethod);return c.release();
}
3、信号槽容器Conntion、ConnectionList、SignalVector、ConnectionData
Connection ->ConnectionList ->SignalVector ->ConnectionData
,这四个结构体都定义在QObjectPrivate类中。
ConnectionData
是每个QObject对象中的成员connections
的类型,信号所在对象中的connections
存储了该对象所有信号和信号连接的槽函数的信息。SignalVector
是ConnectionData
中的成员signalVector
的类型,通过信号索引得到一个信号对应的ConnectionList
。ConnectList
是Qt中用于管理信号和槽连接的数据结构,它跟踪连接信息并在信号发射时找到对应的槽函数进行调用,ConnectionList
与 一 一 信号对应。ConnectList
中的每个条目Conntion
都表示一个连接,包含信号发送者、信号索引、槽接收者和槽索引等信息,Conntion
与connect()
一 一 对应。
在connect()
中通过ObjectPrivate::get(s)->addConnection(signal_index, c.get())
将新建的connection
添加到信号槽容器connections
中,首先得到QObjectPrivate成员ConnectionData
对象connections
,通过信号索引得到ConnectionData
中的SignalVector
成员signalVector
中的信号对应的ConnectionList
,将connection
添加ConnectionList
的结尾。
struct Connection : public ConnectionOrSignalVector
{Connection **prev;QAtomicPointer<Connection> nextConnectionList;Connection *prevConnectionList;QObject *sender;QAtomicPointer<QObject> receiver;//.....
};
结构体ConnectionList
是单向链表
// ConnectionList is a singly-linked liststruct ConnectionList {QAtomicPointer<Connection> first;QAtomicPointer<Connection> last;};
struct SignalVector : public ConnectionOrSignalVector
{quintptr allocated;// ConnectionList signals[]ConnectionList &at(int i{//............}const ConnectionList &at(int i) const{//........................}int count() const { return static_cast<int>(allocated); }
};
struct ConnectionData {// the id below is used to avoid activating new connections. When the object gets// deleted it's set to 0, so that signal emission stopsQAtomicInteger<uint> currentConnectionId;QAtomicInt ref;QAtomicPointer<SignalVector> signalVector;Connection *senders = nullptr;Sender *currentSender = nullptr; // object currently activating the objectQAtomicPointer<Connection> orphaned;~ConnectionData(){//......}// must be called on the senders connection data// assumes the senders and receivers lock are heldvoid removeConnection(Connection *c);void cleanOrphanedConnections(QObject *sender) {///.....}void cleanOrphanedConnectionsImpl(QObject *sender);ConnectionList &connectionsForSignal(int signal) {///.....}void resizeSignalVector(uint size) {///.....}int signalVectorCount() const {//...}static void deleteOrphaned(ConnectionOrSignalVector *c);};
二、函数指针链接(qt5后)
编译时检查,拼写错误编译不通过;使用函数指针作为槽函数参数
//connect to a function pointer (not a member)template <typename Func1, typename Func2>static inline typename std::enable_if<int(QtPrivate::FunctionPointer<Func2>::ArgumentCount) >= 0 &&!QtPrivate::FunctionPointer<Func2>::IsPointerToMemberFunction, QMetaObject::Connection>::typeconnect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal, const QObject *context, Func2 slot,Qt::ConnectionType type = Qt::AutoConnection)
三、Lamba表达式
- 编译时检查
- 不支持信号重载,支持槽函数重载
- 默认directConnection
触发
在定义信号后,MOC编译器会在moc文件中定义该信号的信号函数。
// SIGNAL 0
void QDBusAdaptorConnector::relaySignal(QObject * _t1, const QMetaObject * _t2, int _t3, const QVariantList & _t4)
{
//----------_a是返回值和参数--------------------void *_a[] = { 0, const_cast<void*>(reinterpret_cast<const void*>(&_t1)), const_cast<void*>(reinterpret_cast<const void*>(&_t2)), const_cast<void*>(reinterpret_cast<const void*>(&_t3)), const_cast<void*>(reinterpret_cast<const void*>(&_t4)) };QMetaObject::activate(this, &staticMetaObject, 0, _a);
}
当在程序中发射一个信号,是通过emit
该信号实现的,emit
是一个宏,定义为空白。
# define emit
发射信号实际上是就是调用这个信号函数,在这个函数中调用activate()
来触发连接的槽函数。
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()))doActivate<true>(sender, signal_index, argv);elsedoActivate<false>(sender, signal_index, argv);
}
在得到绝对索引后,调用doActivate()
。
- 信号所在对象是否为阻塞状态,若是直接返回。
- 检查是否存在信号槽连接,没有返回。
- 获取信号所在对象中该信号对应的
connection
容器connectList
。 - 获取当前线程,并判断发送线程和信号所在对象的线程是否是同一线程。
- 遍历
connectionlist
,依次触发该信号所有链接的槽函数。
在嵌套的两个doWhile
循环中遍历connectionlist
,根据发送线程和接收者线程的关系进行不同的操作:
- 如果是自动连接而且发送线程和接收者线程不一样,或者是队列链接,则使用
queued_activated()
去post
一个新建的QMetaCallEvent
调用事件到接收者线程的事件队列。 - 如果是阻塞队列连接,当发送线程和接收者在同一个线程警告检测到死锁,否则新建一个
QMetaCallEvent
调用事件,通过postEvent
函数post
到接收者线程的事件队列中。 - 如果不是以上两种情况,即发送线程和接收者线程为同一线程,直接调用槽函数。
template <bool callbacks_enabled>
void doActivate(QObject *sender, int signal_index, void **argv)
{QObjectPrivate *sp = QObjectPrivate::get(sender);
//------------判断是否为阻塞状态--------------------------if (sp->blockSig)return;Q_TRACE_SCOPE(QMetaObject_activate, sender, signal_index);
//----------------检查是否存在信号槽链接--------------------------------if (sp->isDeclarativeSignalConnected(signal_index)&& 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 connectedif (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);//-------获取信号所在对象中该信号对应的connection容器connectList------------------bool senderDeleted = false;{Q_ASSERT(sp->connections.loadAcquire());QObjectPrivate::ConnectionDataPointer connections(sp->connections.loadRelaxed());QObjectPrivate::SignalVector *signalVector = connections->signalVector.loadRelaxed();const QObjectPrivate::ConnectionList *list;if (signal_index < signalVector->count())list = &signalVector->at(signal_index);elselist = &signalVector->at(-1);
//-----------------------------------获取线程id-------------------------------Qt::HANDLE currentThreadId = QThread::currentThreadId();//获取当前线程,并判断是否是发送者的线程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.
//-------------遍历connectionlist,依次触发该信号所有链接的槽函数-----------------uint highestConnectionId = connections->currentConnectionId.loadRelaxed();do {QObjectPrivate::Connection *c = list->first.loadRelaxed();if (!c)continue;do {//获取接收者线程数据QObject * const receiver = c->receiver.loadRelaxed();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//信号所在线程和发送者不在一个线程,在获取connection中的接收者线程时上锁QMutexLocker lock(signalSlotLock(receiver));receiverInSameThread = currentThreadId == td->threadId.loadRelaxed();}//--------------如果是多线程连接,即队列和阻塞队列连接-------------------------// determine if this connection should be sent immediately or// put into the event queue//如果是自动连接而且发送线程和接收者线程不一样,或者是队列链接,使用queued_activated调用槽函数if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)|| (c->connectionType == Qt::QueuedConnection)) {queued_activate(sender, signal_index, c, argv);continue;
#if QT_CONFIG(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);}//postEvent发送一个qmetacallevent事件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);if (c->isSlotObject) {c->slotObj->ref();struct Deleter {void operator()(QtPrivate::QSlotObjectBase *slot) const {if (slot) slot->destroyIfLastRef();}};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()) {//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);}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));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);}
}