概述
connect實質上是将對象A的信号和對象B的槽函數進行連接配接,然後傳回一個句柄Connection。
正文
下面通過源碼來解析一下:(注意看中文注釋)
// connection表示信号槽連接配接句柄
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(nullptr);
}
QByteArray tmp_signal_name;
if (!check_signal_macro(sender, signal, "connect", "bind"))
return QMetaObject::Connection(nullptr);
const QMetaObject *smeta = sender->metaObject();// 發送者元對象
const char *signal_arg = signal;
++signal; //skip code
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());
}
if (signal_index < 0) {
// 沒有索引,傳回空句柄
err_method_notfound(sender, signal_arg, "connect");
err_info_about_objects("connect", sender, receiver);
return QMetaObject::Connection(nullptr);
}
signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);// 擷取原生方法的索引(例如傳遞destroyed()的索引,則傳回destroyed(QObject*)的索引
signal_index += QMetaObjectPrivate::signalOffset(smeta);
QByteArray tmp_method_name;
int membcode = extract_code(method); // 擷取方法code
// 校驗方法code,如果該方法code不屬于信号和槽函數,則傳回空句柄
if (!check_method_code(membcode, receiver, method, "connect"))
return QMetaObject::Connection(nullptr);
const char *method_arg = method;
++method; // skip code
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) {
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 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(nullptr);
}
// 校驗信号和槽的參數
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(nullptr);
}
// 當連結類型為隊列時需要檢測信号參數的類型是否為元類型,不是的話需要注冊,沒有注冊就無法連結
int *types = nullptr;
if ((type == Qt::QueuedConnection)
&& !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {
return QMetaObject::Connection(nullptr);
}
#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));
return handle;
}
總結來說,connect前面主要是進行校驗發送者、信号索引、接收者、槽函數索引、連結類型以及參數類型。都校驗成功時,傳回句柄。
connection句柄是非常有用的,裡面包含了發送者、信号索引、發送者元對象、接收者、槽函數索引、接收者元對象、連結類型、參數類型;後續在執行信号函數的時候會檢索對應信号的所有資訊。