信号槽通过connect进行连接,connect的源码在qobject.cpp中
QT4中的connect的声明如下
static QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *member, Qt::ConnectionType = Qt::AutoConnection);
使用时,一般是这样的
connect(sender, SIGNAL(destroyed()), this, SLOT(objectDestroyed()));
其中的SIGNAL和SLOT定义如下,在qobjectdefs.h中
# define SLOT(a) "1"#a
# define SIGNAL(a) "2"#a
#a 得到参数的字符串形式,对于槽函数的宏,前面加 "1",对于信号的宏,前面加 "2",则上面的connect代码就变成如下形式
connect(sender, "2destroyed()", this, "1objectDestroyed()");
connect具体实现如下
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);
}//检查sender、receiver、signal、method是否为空,为空,返回个空连接
QByteArray tmp_signal_name;
if (!check_signal_macro(sender, signal, "connect", "bind"))//检查signal字符串中是否有2,如果没有,空连接返回
return QMetaObject::Connection(0);
const QMetaObject *smeta = sender->metaObject();
const char *signal_arg = signal;
++signal; //跳过2,指向具体的字符串
QArgumentTypeArray 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 signatures
tmp_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());
}//如果相对序号不能存在,normalized后,再计算一次信号函数的相对序号
if (signal_index < 0) {
err_method_notfound(sender, signal_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return QMetaObject::Connection(0);
}//如果normalized后,信号函数还不存在,返回空连接
signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
signal_index += QMetaObjectPrivate::signalOffset(smeta);//将signal_index加上偏移,变为绝对序号
QByteArray tmp_method_name;
int membcode = extract_code(method);
if (!check_method_code(membcode, receiver, method, "connect"))
return QMetaObject::Connection(0);//检查槽函数是否有字符1
const char *method_arg = method;
++method; //处理方法同信号
QArgumentTypeArray 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) {//根据membcode是1还是2计算槽函数的相对序号,因为信号可以连接信号,所以在考虑槽函数的时候要处理槽函数是信号的情况
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) {//如果得到的槽函数的相对序号不合法,normalized并再计算槽函数的相对序号
// check for normalized methods
tmp_method_name = QMetaObject::normalizedSignature(method);
method = tmp_method_name.constData();
methodTypes.clear();
methodName = QMetaObjectPrivate::decodeMethodSignature(method, methodTypes);
// rmeta may have been modified above
rmeta = 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);
}//如果此时信号槽的连接方式是QueuedConnection,但是queuedConnectionTypes的返回值为空(信号的类型数据没有获取到),那么也返回一个空连接
#ifndef QT_NO_DEBUG
QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
check_and_warn_compat(smeta, smethod, rmeta, rmethod);
#endif
QMetaObject::Connection handle = QMetaObject::Connection(QMetaObjectPrivate::connect(
sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));//前面的check都没问题,进行连接,调用QMetaObjectPrivate::connect
return handle;
}
QMetaObjectPrivate::connect也在qobject.cpp中
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)
//sender 是发送信号的对象;signal_index 信号绝对序号;smeta 是发送信号的对象的元对象;
//receiver 是信号的接收对象;method_index 是槽函数的相对序号;rmeta 是信号的接收对象的元对象;
//type 是连接类型;types 是指向的是入队时,信号的类型数据。
{
QObject *s = const_cast<QObject *>(sender);
QObject *r = const_cast<QObject *>(receiver);//去底层const
int method_offset = rmeta ? rmeta->methodOffset() : 0;//加上偏移量之后,得到槽函数的绝对序号
Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);
QObjectPrivate::StaticMetaCallFunction callFunction = rmeta ? rmeta->d.static_metacall : nullptr;//函数指针指向接收端元对象的qt_static_metacall(在MOC文件中实现)
QOrderedMutexLocker locker(signalSlotLock(sender),
signalSlotLock(receiver));//互斥锁,为了多线程顺序访问
QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.loadRelaxed();//获取sender的ConnectionData
if (type & Qt::UniqueConnection && scd) {//如果连接类型有UniqueConnection并且sender的ConnectionData有数据
if (scd->signalVectorCount() > signal_index) {//判断信号的绝对索引小于信号的数量
const 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();
}//遍历ConnectionList,如果发现ConnectionList中的receiver和传入的receiver相等并且该connection中的方法索引和绝对索引相等,那么就认为不满足UniqueConnection,直接退出
}
type &= Qt::UniqueConnection - 1;
}
std::unique_ptr<QObjectPrivate::Connection> c{new QObjectPrivate::Connection};//创建一个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;
QObjectPrivate::get(s)->addConnection(signal_index, c.get());//根据信号的绝对序号,添加QObjectPrivate::Connection对象
locker.unlock();//解锁
QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index);
if (smethod.isValid())
s->connectNotify(smethod);
return c.release();
}
上述代码中的loadrelaxed调用的其实就是C++11中的atomic类中的load方法,返回的是其中类或结构中包含的数据,详细见http://www.cplusplus.com/reference/atomic/atomic/load/
addConnection实现如下
void QObjectPrivate::addConnection(int signal, Connection *c)
{
Q_ASSERT(c->sender == q_ptr);
ensureConnectionData();
ConnectionData *cd = connections.loadRelaxed();
cd->resizeSignalVector(signal + 1);//为新信号的Connectionlist分配内存
ConnectionList &connectionList = cd->connectionsForSignal(signal);//找到信号的connectionList
if (connectionList.last.loadRelaxed()) {//添加新的信号的connectionlist
Q_ASSERT(connectionList.last.loadRelaxed()->receiver.loadRelaxed());
connectionList.last.loadRelaxed()->nextConnectionList.storeRelaxed(c);
} else {
connectionList.first.storeRelaxed(c);
}
c->id = ++cd->currentConnectionId;//设置新的connection的id
c->prevConnectionList = connectionList.last.loadRelaxed();//设置新的connection的prevConnectionList
connectionList.last.storeRelaxed(c);
QObjectPrivate *rd = QObjectPrivate::get(c->receiver.loadRelaxed());
rd->ensureConnectionData();
c->prev = &(rd->connections.loadRelaxed()->senders);
c->next = *c->prev;//将新的connection添加到对应的信号的connectionlist中
*c->prev = c;
if (c->next)
c->next->prev = &c->next;
}
connection与connectionlist的整体的结构图如下
参考
Qt5.14源码
欢迎大家评论交流,作者水平有限,如有错误,欢迎指出