信號槽連接
目錄- 信號槽連接
- 1. 信號的連接
- 2 槽的呼叫
信號槽的連接,其實內部本質還是一個回呼函式,主要是維護了信號發送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的事件到事件回圈中,然后由事件回圈去觸發槽函式的呼叫,
好了,對于信號和槽的分析,我們暫時就先分析到這,如果有問題是我上面沒有說明的,可以在評論區給我評論,我看到了,看懂了,我就會更新這篇博客的,
謝謝觀看 ??
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/548142.html
標籤:其他
