本篇探析signal slot的连接和调用是如何实现的。
宏SLOT,SIGNAL
在qobjectdefs.h中有这样的定义:
# define METHOD(a) "0"#a
# define SLOT(a) "1"#a
# define SIGNAL(a) "2"#a
不过是在方法签名之前加了一个数字标记。因为我们既可以将signal连接到slot,也可以将signal连接到signal,所有必须要有某种方法区分一下。
QObject::connect()
- bool QObject::connect(const QObject *sender, const char *signal,
- const QObject *receiver, const char *method,
- Qt::ConnectionType type)
- {
- {
- const void *cbdata[] = { sender, signal, receiver, method, &type };
- if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
- return true;
- }
- #ifndef QT_NO_DEBUG
- bool warnCompat = true;
- #endif
- if (type == Qt::AutoCompatConnection) {
- type = Qt::AutoConnection;
- #ifndef QT_NO_DEBUG
- warnCompat = false;
- #endif
- }
- if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
- qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
- sender ? sender->metaObject()->className() : "(null)",
- (signal && *signal) ? signal+1 : "(null)",
- receiver ? receiver->metaObject()->className() : "(null)",
- (method && *method) ? method+1 : "(null)");
- return false;
- }
- QByteArray tmp_signal_name;
- if (!check_signal_macro(sender, signal, "connect", "bind"))
- return false;
- const QMetaObject *smeta = sender->metaObject();
- const char *signal_arg = signal;
- ++signal;
- int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal);
- if (signal_index < 0) {
-
- tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
- signal = tmp_signal_name.constData() + 1;
- smeta = sender->metaObject();
- signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal);
- if (signal_index < 0) {
- err_method_notfound(sender, signal_arg, "connect");
- err_info_about_objects("connect", sender, receiver);
- return false;
- }
- }
- signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
- int signalOffset, methodOffset;
- computeOffsets(smeta, &signalOffset, &methodOffset);
- int signal_absolute_index = signal_index + methodOffset;
- signal_index += signalOffset;
- QByteArray tmp_method_name;
- int membcode = extract_code(method);
- if (!check_method_code(membcode, receiver, method, "connect"))
- return false;
- const char *method_arg = method;
- ++method;
- const QMetaObject *rmeta = receiver->metaObject();
- int method_index = -1;
- switch (membcode) {
- case QSLOT_CODE:
- method_index = rmeta->indexOfSlot(method);
- break;
- case QSIGNAL_CODE:
- method_index = rmeta->indexOfSignal(method);
- break;
- }
- if (method_index < 0) {
-
- tmp_method_name = QMetaObject::normalizedSignature(method);
- method = tmp_method_name.constData();
- switch (membcode) {
- case QSLOT_CODE:
- method_index = rmeta->indexOfSlot(method);
- break;
- case QSIGNAL_CODE:
- method_index = rmeta->indexOfSignal(method);
- break;
- }
- }
- if (method_index < 0) {
- err_method_notfound(receiver, method_arg, "connect");
- err_info_about_objects("connect", sender, receiver);
- return false;
- }
- if (!QMetaObject::checkConnectArgs(signal, method)) {
- qWarning("QObject::connect: Incompatible sender/receiver arguments"
- "\n %s::%s --> %s::%s",
- sender->metaObject()->className(), signal,
- receiver->metaObject()->className(), method);
- return false;
- }
- int *types = 0;
- if ((type == Qt::QueuedConnection || type == Qt::BlockingQueuedConnection)
- && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))
- return false;
- #ifndef QT_NO_DEBUG
- {
- QMetaMethod smethod = smeta->method(signal_absolute_index);
- QMetaMethod rmethod = rmeta->method(method_index);
- if (warnCompat) {
- if(smethod.attributes() & QMetaMethod::Compatibility) {
- if (!(rmethod.attributes() & QMetaMethod::Compatibility))
- qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)", smeta->className(), signal);
- } else if(rmethod.attributes() & QMetaMethod::Compatibility && membcode != QSIGNAL_CODE) {
- qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)",
- smeta->className(), signal, rmeta->className(), method);
- }
- }
- }
- #endif
- if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index, type, types))
- return false;
- const_cast(sender)->connectNotify(signal - 1);
- return true;
- }
忽略细节,只关注主要的流程,这段代码的做的事情就是将signal在sender的meta
system中的signal索引找出,以及接受者方法(signal或slot)在receiver的meta
system中的索引找出来。在委托QMetaObjectPrivate::connect()执行连接。
- bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
- const QObject *receiver, int method_index, int type, int *types)
- {
- QObject *s = const_cast(sender);
- QObject *r = const_cast(receiver);
- QOrderedMutexLocker locker(signalSlotLock(sender),
- signalSlotLock(receiver));
- if (type & Qt::UniqueConnection) {
- QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
- if (connectionLists && connectionLists->count() > signal_index) {
- const QObjectPrivate::Connection *c2 =
- (*connectionLists)[signal_index].first;
- while (c2) {
- if (c2->receiver == receiver && c2->method == method_index)
- return false;
- c2 = c2->nextConnectionList;
- }
- }
- type &= Qt::UniqueConnection - 1;
- }
- QObjectPrivate::Connection *c = new QObjectPrivate::Connection;
- c->sender = s;
- c->receiver = r;
- c->method = method_index;
- c->connectionType = type;
- c->argumentTypes = types;
- c->nextConnectionList = 0;
- QT_TRY {
- QObjectPrivate::get(s)->addConnection(signal_index, c);
- } QT_CATCH(...) {
- delete c;
- QT_RETHROW;
- }
- c->prev = &(QObjectPrivate::get(r)->senders);
- c->next = *c->prev;
- *c->prev = c;
- if (c->next)
- c->next->prev = &c->next;
- QObjectPrivate *const sender_d = QObjectPrivate::get(s);
- if (signal_index < 0) {
- sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0;
- } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) {
- sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f));
- }
- return true;
- }
同样忽略细节,这段代码首先在connecttype要求UniqueConnection的时候检查一下是不是有重复的连接。然后创建一个
QObjectPrivate::Connection结构,这个结构包含了sender,receiver,接受方法的method_index,然后
加入到某个连接存储表中;连接存储表可能是一种hash结构,signal_index就是key。可以推测,当signal方法被调用时,一定会到这个
结构中查找所有连接到该signal的connection,connection保存了receiver和method
index,由上一篇可知,可以很容易调用到receiver的对应method。