信號槽連接 信號槽的連接,其實內部本質還是一個回調函數,主要是維護了信號發送Object的元對象里一個連接的列表。調用connect函數時,將槽的一系列信息,封裝成一個Connection,在發送信號時,通過這個列表,去回調槽函數。 1. 信號的連接 下麵列舉一種信號的連接方式,來大致講解一下信號的 ...
信號槽連接
目錄信號槽的連接,其實內部本質還是一個回調函數,主要是維護了信號發送Object的元對象里一個連接的列表。調用connect
函數時,將槽的一系列信息,封裝成一個Connection
,在發送信號時,通過這個列表,去回調槽函數。
1. 信號的連接
下麵列舉一種信號的連接方式,來大致講解一下信號的連接過程。
//Connect a signal to a pointer to qobject member function
// QtPrivate::FunctionPointer<Func1>::Object返回發送信號的對象類型
template <typename Func1, typename Func2>
static inline QMetaObject::Connection connect(const typename QtPrivate::FunctionPointer<Func1>::Object *sender, Func1 signal,
const typename QtPrivate::FunctionPointer<Func2>::Object *receiver, Func2 slot,
Qt::ConnectionType type = Qt::AutoConnection)
{
typedef QtPrivate::FunctionPointer<Func1> SignalType;
typedef QtPrivate::FunctionPointer<Func2> SlotType;
Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<typename SignalType::Object>::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<typename SignalType::Arguments, typename SlotType::Arguments>::value),
"Signal and slot arguments are not compatible.");
// 檢查信號和槽的返回值是否相容
Q_STATIC_ASSERT_X((QtPrivate::AreArgumentsCompatible<typename SlotType::ReturnType, typename SignalType::ReturnType>::value),
"Return type of the slot is not compatible with the return type of the signal.");
const int *types = nullptr;
// SignalType -> QtPrivate::FunctionPointer<Func1>
// QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types() 返回信號參數的值對應的元類型id列表
if (type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
types = QtPrivate::ConnectionTypes<typename SignalType::Arguments>::types();
return connectImpl(sender, reinterpret_cast<void **>(&signal),
receiver, reinterpret_cast<void **>(&slot),
new QtPrivate::QSlotObject<Func2, typename QtPrivate::List_Left<typename SignalType::Arguments, SlotType::ArgumentCount>::Value,
typename SignalType::ReturnType>(slot),
type, types, &SignalType::Object::staticMetaObject);
}
上面主要都是一些基本的信號連接的判斷,主要是:
- 信號和槽的參數數量
- 信號和槽的參數是否相容
- 信號和槽的返回值是否相容
然後獲取信號參數所對應的元類型Id,再就到了一個信號連接的具體內部實現中
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 nullptr parameter");
if (slotObj)
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
int signal_index = -1;
void *args[] = { &signal_index, signal };
// 根據調用來判斷是否存在信號,如果當前類沒有就去父類中尋找
// 直到找到信號或者是最基層的類
// 找到信號的index和信號的對象
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(nullptr);
}
// 信號下標
signal_index += QMetaObjectPrivate::signalOffset(senderMetaObject);
return QObjectPrivate::connectImpl(sender, signal_index, receiver, slot, slotObj, type, types, senderMetaObject);
}
同樣,我們對這個函數進行分析,第一個片段是對信號發送者是否為空指針的一個判斷
if (!signal) {
qWarning("QObject::connect: invalid nullptr parameter");
if (slotObj)
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
第二個片段是去找到信號發送者(sender)的元對象類型(Meta Object)以及信號在對象信號中的位置。如果當前對象沒有該信號,就去其父類對象去找。直到找到為止。
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;
}
然後就是進一步調用其內部實現:
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)
{
// 發送對象、接收對象、槽函數對象、信號發送的元對象都不為空 2023-3-11
if (!sender || !receiver || !slotObj || !senderMetaObject) {
// 任意一個為空,報錯且清理空間,並返回
const char *senderString = sender ? sender->metaObject()->className()
: senderMetaObject ? senderMetaObject->className()
: "Unknown";
const char *receiverString = receiver ? receiver->metaObject()->className()
: "Unknown";
qWarning("QObject::connect(%s, %s): invalid nullptr parameter", senderString, receiverString);
if (slotObj)
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
// 去掉const的發送和接受對象
QObject *s = const_cast<QObject *>(sender);
QObject *r = const_cast<QObject *>(receiver);
// 順序鎖,按照順序依次去對mutex去上鎖
// 這裡依次對發送和接收者的信號去上鎖
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));
if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.loadRelaxed()) {
// ObjectPrivate::get(s) 獲取s對應的d指針
// connections 維護了所有的信號槽連接
QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.loadRelaxed();
if (connections->signalVectorCount() > signal_index) {
// 獲取信號的連接
const QObjectPrivate::Connection *c2 = connections->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
// 迴圈遍歷
while (c2) {
// 如果已經存在信號和槽的連接,且為uniqueConnection,則返回
if (c2->receiver.loadRelaxed() == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
c2 = c2->nextConnectionList.loadRelaxed();
}
}
// 將type與UniqueConnection進行異或,去掉UniqueConnection
type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection);
}
// 創建一個新的連接
std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};
c->sender = s;
c->signal_index = signal_index;
QThreadData *td = r->d_func()->threadData;
td->ref();
c->receiverThreadData.storeRelaxed(td);
c->receiver.storeRelaxed(r);
c->slotObj = slotObj;
c->connectionType = type;
c->isSlotObject = true;
if (types) {
c->argumentTypes.storeRelaxed(types);
c->ownArgumentTypes = false;
}
// 將新創建的連接加到連接列表中
QObjectPrivate::get(s)->addConnection(signal_index, c.get());
QMetaObject::Connection ret(c.release());
locker.unlock();
QMetaMethod method = QMetaObjectPrivate::signal(senderMetaObject, signal_index);
Q_ASSERT(method.isValid());
s->connectNotify(method);
return ret;
}
同樣第一個部分也是對一些個空值的判斷
// 發送對象、接收對象、槽函數對象、信號發送的元對象都不為空 2023-3-11
if (!sender || !receiver || !slotObj || !senderMetaObject) {
// 任意一個為空,報錯且清理空間,並返回
const char *senderString = sender ? sender->metaObject()->className()
: senderMetaObject ? senderMetaObject->className()
: "Unknown";
const char *receiverString = receiver ? receiver->metaObject()->className()
: "Unknown";
qWarning("QObject::connect(%s, %s): invalid nullptr parameter", senderString, receiverString);
if (slotObj)
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
然後就是一個if判斷,主要是對Qt::UniqueConnection
連接的一些處理,獲取當前對象的信號連接列表,並判斷當前要連接的信號和槽,之前有沒有被連接過,如果有過連接,就直接返回。
if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.loadRelaxed()) {
// ObjectPrivate::get(s) 獲取s對應的d指針
// connections 維護了所有的信號槽連接
QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.loadRelaxed();
if (connections->signalVectorCount() > signal_index) {
// 獲取信號的連接
const QObjectPrivate::Connection *c2 = connections->signalVector.loadRelaxed()->at(signal_index).first.loadRelaxed();
// 迴圈遍歷
while (c2) {
// 如果已經存在信號和槽的連接,且為uniqueConnection,則返回
if (c2->receiver.loadRelaxed() == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
c2 = c2->nextConnectionList.loadRelaxed();
}
}
// 將type與UniqueConnection進行異或,去掉UniqueConnection
type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection);
}
最後才是創建一個Connection
並將連接的信息以及信號的參數設置進去,然後保存到對象的信號連接容器里。
// 創建一個新的連接
std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};
c->sender = s;
c->signal_index = signal_index;
QThreadData *td = r->d_func()->threadData;
td->ref();
c->receiverThreadData.storeRelaxed(td);
c->receiver.storeRelaxed(r);
c->slotObj = slotObj;
c->connectionType = type;
c->isSlotObject = true;
if (types) {
c->argumentTypes.storeRelaxed(types);
c->ownArgumentTypes = false;
}
// 將新創建的連接加到連接列表中
QObjectPrivate::get(s)->addConnection(signal_index, c.get());
QMetaObject::Connection ret(c.release());
locker.unlock();
QMetaMethod method = QMetaObjectPrivate::signal(senderMetaObject, signal_index);
Q_ASSERT(method.isValid());
s->connectNotify(method);
return ret;
2 槽的調用
定義一個信號,使用moc
生成moc文件之後,我們可以看到信號函數的定義如下:
// SIGNAL 0
void MainWindow::sgnTestFor()
{
QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}
我們發射一個信號的時候,我們會這樣寫:
emit sgnTestFor();
我們可以看關於emit
的定義:
其實emit
關鍵字什麼都沒有做,只是標識了一下當前發射了信號。所以本質上,發射一個信號實際上就是直接調用了這個信號的函數,也就是調用了QMetaObject
中的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);
else
doActivate<false>(sender, signal_index, argv);
}
上面的qt_signal_spy_callback_set
暫時不清楚是什麼玩意,所以我們不管,直接看具體的doActive
函數
template <bool callbacks_enabled>
void doActivate(QObject *sender, int signal_index, void **argv)
{
// 首先獲取QObject的private對象
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 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();
// 信號連接列表,因為一個信號可能連接了多個槽
const QObjectPrivate::ConnectionList *list;
if (signal_index < signalVector->count())
list = &signalVector->at(signal_index);
else
list = &signalVector->at(-1);
// 判斷當前線程是不是信號發送者的線程
Qt::HANDLE currentThreadId = QThread::currentThreadId();
bool inSenderThread = currentThreadId == QObjectPrivate::get(sender)->threadData.loadRelaxed()->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();
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
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(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);
// 如果是槽函數對象
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是迴圈遍歷信號所連接的槽/信號
} 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);
}
}
前面的一些基本的判斷,我們就忽略,直接找到重要的地方,迴圈遍歷信號所連接的部分。
-
當信號槽為隊列連接,我們需要將信號丟到事件迴圈里,待事件迴圈將該信號發送出去。
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread) || (c->connectionType == Qt::QueuedConnection)) { queued_activate(sender, signal_index, c, argv); continue; #if QT_CONFIG(thread) }
-
當信號槽為阻塞隊列連接(
BlockingQueuedConnection
)時,首先,我們需要判斷發送和接收者是不是在一個線程,因為如果連接類型為BlockingQueuedConnection
,發送者和接收者在一個線程,會導致死鎖。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 }
其他類型的連接如下:
-
信號的連接是一個槽函數對象
QSlotObject
,就直接調用call
函數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); } }
-
如果是其他類型,就通過
QMetaObject::InvokeMetaMethod
來調用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); }
並且遍歷整個列表,將所有相關的連接都調用一遍。
然後我們看QueuedConnection
的連接函數:
代碼里,揭示了一點,就是如果我們使用信號槽連接的方式,而信號的參數不是一個元類型或者沒用qRegisterMetaType
來註冊類型,那麼隊列連接是不行的,槽函數是不會觸發的。
static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
{
// 存儲元類型參數(meta-type argument)
const int *argumentTypes = c->argumentTypes.loadRelaxed();
if (!argumentTypes) {
// 獲取對應的信號
QMetaMethod m = QMetaObjectPrivate::signal(sender->metaObject(), signal);
// 獲取信號的參數,並檢查是否所有參數均為元類型(meta-type)
argumentTypes = queuedConnectionTypes(m.parameterTypes());
if (!argumentTypes) // cannot queue arguments
argumentTypes = &DIRECT_CONNECTION_ONLY;
if (!c->argumentTypes.testAndSetOrdered(nullptr, 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();
// 然後通過post一個QMetaCallEvent事件到事件迴圈隊列中去
QMetaCallEvent *ev = c->isSlotObject ?
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];
for (int n = 1; n < nargs; ++n)
args[n] = QMetaType::create(types[n], argv[n]);
}
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;
}
QCoreApplication::postEvent(c->receiver.loadRelaxed(), ev);
}
代碼中我們可以看到,這裡是通過post一個QMetaCallEvent
的事件到事件迴圈中,然後由事件迴圈去觸發槽函數的調用。
好了,對於信號和槽的分析,我們暫時就先分析到這,如果有問題是我上面沒有說明的,可以在評論區給我評論,我看到了,看懂了,我就會更新這篇博客的。
謝謝觀看