QT信号与槽的原理

QT信号与槽的原理,第1张

QT信号与槽的原理

本文从源码的角度来分析信号与槽的原理。

首先当我们在头文件中增加了关键字Q_OBJECT后moc工具(meta-object-Compiler元对象编译器)会将根据头文件内容生成一个moc的链接文件。

我们还是使用官方的例程进行讲解。
Counter.h

#ifndef COUNTER_H
#define COUNTER_H
#include 
#include 

class Counter : public QObject
{
    Q_OBJECT
    int m_value;

public:
    int value() const {return m_value;}

public slots:
    void setValue(int value);

signals:
    void valueChanged(int newValue);

public:
    Counter();
    ~Counter();
};

#endif // COUNTER_H

Counter.cpp

#include "counter.h"

Counter::Counter()
{
    m_value = 0;
}

void Counter::setValue(int value)
{
    if(value != m_value)
    {
        m_value = value;
        emit valueChanged(value);
    }
}

Counter::~Counter()
{

}

main.c

#include "counter.h"

int main()
{
    Counter a, b;
    QObject::connect(&a, SIGNAL(valueChanged(int)), &b, SLOT(setValue(int)));
    qDebug() << "First :b.value() is " << b.value();
    a.setValue(12);
    qDebug() << "Second:b.value() is " << b.value();
    return 0;
}

moc_counter.cpp


#include "../../Test/counter.h"
#include 
#include 
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'counter.h' doesn't include ."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using the moc from 5.4.0. It"
#error "cannot be used with the include files from this version of Qt."
#error "(The moc has changed too much.)"
#endif

QT_BEGIN_MOC_NAMESPACE
struct qt_meta_stringdata_Counter_t {
    QByteArrayData data[6];
    char stringdata[46];
};
#define QT_MOC_LITERAL(idx, ofs, len) 
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, 
    qptrdiff(offsetof(qt_meta_stringdata_Counter_t, stringdata) + ofs 
        - idx * sizeof(QByteArrayData)) 
    )
static const qt_meta_stringdata_Counter_t qt_meta_stringdata_Counter = {
    {
QT_MOC_LITERAL(0, 0, 7), // "Counter"
QT_MOC_LITERAL(1, 8, 12), // "valueChanged"
QT_MOC_LITERAL(2, 21, 0), // ""
QT_MOC_LITERAL(3, 22, 8), // "newValue"
QT_MOC_LITERAL(4, 31, 8), // "setValue"
QT_MOC_LITERAL(5, 40, 5) // "value"

    },
    "CountervalueChangednewValuesetValue"
    "value"
};
#undef QT_MOC_LITERAL

static const uint qt_meta_data_Counter[] = {

 // content:
       7,       // revision
       0,       // classname
       0,    0, // classinfo
       2,   14, // methods
       0,    0, // properties
       0,    0, // enums/sets
       0,    0, // constructors
       0,       // flags
       1,       // signalCount

 // signals: name, argc, parameters, tag, flags
       1,    1,   24,    2, 0x06 ,

 // slots: name, argc, parameters, tag, flags
       4,    1,   27,    2, 0x0a ,

 // signals: parameters
    QmetaType::Void, QmetaType::Int,    3,

 // slots: parameters
    QmetaType::Void, QmetaType::Int,    5,

       0        // eod
};

void Counter::qt_static_metacall(QObject *_o, QmetaObject::Call _c, int _id, void **_a)
{
    if (_c == QmetaObject::InvokemetaMethod) {
        Counter *_t = static_cast(_o);
        switch (_id) {
        case 0: _t->valueChanged((*reinterpret_cast< int(*)>(_a[1]))); break;
        case 1: _t->setValue((*reinterpret_cast< int(*)>(_a[1]))); break;
        default: ;
        }
    } else if (_c == QmetaObject::IndexOfMethod) {
        int *result = reinterpret_cast(_a[0]);
        void **func = reinterpret_cast(_a[1]);
        {
            typedef void (Counter::*_t)(int );
            if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&Counter::valueChanged)) {
                *result = 0;
            }
        }
    }
}

const QmetaObject Counter::staticmetaObject = {
    { &QObject::staticmetaObject, qt_meta_stringdata_Counter.data,
      qt_meta_data_Counter,  qt_static_metacall, Q_NULLPTR, Q_NULLPTR}
};


const QmetaObject *Counter::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicmetaObject() : &staticmetaObject;
}

void *Counter::qt_metacast(const char *_clname)
{
    if (!_clname) return Q_NULLPTR;
    if (!strcmp(_clname, qt_meta_stringdata_Counter.stringdata))
        return static_cast(const_cast< Counter*>(this));
    return QObject::qt_metacast(_clname);
}

int Counter::qt_metacall(QmetaObject::Call _c, int _id, void **_a)
{
    _id = QObject::qt_metacall(_c, _id, _a);
    if (_id < 0)
        return _id;
    if (_c == QmetaObject::InvokemetaMethod) {
        if (_id < 2)
            qt_static_metacall(this, _c, _id, _a);
        _id -= 2;
    } else if (_c == QmetaObject::RegisterMethodArgumentmetaType) {
        if (_id < 2)
            *reinterpret_cast(_a[0]) = -1;
        _id -= 2;
    }
    return _id;
}

// SIGNAL 0
void Counter::valueChanged(int _t1)
{
    void *_a[] = { Q_NULLPTR, const_cast(reinterpret_cast(&_t1)) };
    QmetaObject::activate(this, &staticmetaObject, 0, _a);
}
QT_END_MOC_NAMESPACE

以上就是将counter.h翻译后生成的moc文件。
这里面就做了几件事,分别是:
1、生成了一个qt_meta_stringdata_Counter_t的结构体,一个类型为qt_meta_stringdata_Counter_t的结构体数组qt_meta_stringdata_Counter和一个类型为uint的索引表数组qt_meta_data_Counter。
2、生成了一个回调函数void Counter::qt_static_metacall(QObject *_o, QmetaObject::Call _c, int _id, void **_a),通过它能够执行相关的槽函数或者相关的信号转发。
3、生成了const QmetaObject SignalManager::staticmetaObject的对象,通过它可以找到两个数组和方法回调函数。
4、生成了一个对象调用函数const QmetaObject *Counter::metaObject() const(函数内判断了静态对象staticmetaObject还是动态对象QObject::d_ptr->dynamicmetaObject() )。
5、生成了void *Counter::qt_metacast(const char *_clname)。
6、生成了int Counter::qt_metacall(QmetaObject::Call _c, int _id, void **_a)。
7、生成了信号的函数声明void Counter::valueChanged(int _t1),触发信号后通过调用它,然后从所有信号槽结构体的vector容器中找到相关槽和槽的回调函数,最后通过回调函数来执行槽函数。

现在我们开始来分析源码,首先第一站就是connect中信号和槽的宏定义
#ifndef QT_NO_meta_MACROS
#ifndef QT_NO_DEBUG
# define QLOCATION "" __FILE__ ":" QT_STRINGIFY(__LINE__)
# ifndef QT_NO_KEYWORDS
#  define METHOD(a)   qFlagLocation("0"#a QLOCATION)
# endif
# define SLOT(a)     qFlagLocation("1"#a QLOCATION)
# define SIGNAL(a)   qFlagLocation("2"#a QLOCATION)
#else
# ifndef QT_NO_KEYWORDS
#  define METHOD(a)   "0"#a
# endif
# define SLOT(a)     "1"#a
# define SIGNAL(a)   "2"#a
#endif

这两个宏是将信号与槽函数的名称(函数名以及参数)转换为字符串。在方法名称前面给个标识0,在槽函数前面给个标识1,在信号函数前面给个标识2,这3个标识为后面解析参数是所属哪种类型的起到很大的作用。
还有我们注意到,这里的宏是将信号与槽的名称转换为字符串,这里的名称包括了参数。于是我们可以得出:信号与槽的参数不能包括宏。如果包括宏,是要等里面的宏先展开然后SLOT和SIGNAL再展开还是先SLOT和SIGNAL展开呢?这都是没有定义的。

第二站connect源码,在不改变原文的基础上我把结构理了下。
metaObject::Connection QObject::connect(const QObject *sender, const char *signal,
                                     const QObject *receiver, const char *method,
                                     Qt::ConnectionType type)
{
	//【1】检测参数是否为空,如果为空,返回QmetaObject::Connection(0),类似return 0
    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 QmetaObject::Connection(0);
    }
    QByteArray tmp_signal_name;

	//【2】检查信号的有效性,如果无效,返回QmetaObject::Connection(0),类似return 0
    if (!check_signal_macro(sender, signal, "connect", "bind"))
        return QmetaObject::Connection(0);
        
    //【3】获取发送方的meteobject,实际上就是上面讲到的staticmetaObject对象和QObject::d_ptr->dynamicmetaObject() 
    const QmetaObject *smeta = sender->metaObject();
    const char *signal_arg = signal;
    ++signal; //skip code
    QArgumentTypeArray signalTypes;
    Q_ASSERT(QmetaObjectPrivate::get(smeta)->revision >= 7);
    
    //【4】将信号的名称与参数分开,获取到信号名称和各个参数
    QByteArray signalName = QmetaObjectPrivate::decodeMethodSignature(signal, signalTypes);
    
    //【5】获取信号的索引
    int signal_index = QmetaObjectPrivate::indexOfSignalRelative(
            &smeta, signalName, signalTypes.size(), signalTypes.constData());
    
    //【6】信号索引值判断,如果小于0,去除特殊符号,重新从本地信号中获取,然后信号的名称与参数分开、获取发送方的meteobject、 获取信号的索引。    
    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());
    }
    //【7】信号的索引小于0,则提示错误信息,然后return。
    if (signal_index < 0) {
        err_method_notfound(sender, signal_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return QmetaObject::Connection(0);
    }
    //【8】这步具体我也不是很理解,大概意思是将索引重新计算了一次。
    signal_index = QmetaObjectPrivate::originalClone(smeta, signal_index);
    signal_index += QmetaObjectPrivate::signalOffset(smeta);

    QByteArray tmp_method_name;
    //【9】获取方法类型,即接收方是槽函数还是信号转发
    int membcode = extract_code(method);

	//【10】检测方法有效性。
    if (!check_method_code(membcode, receiver, method, "connect"))
        return QmetaObject::Connection(0);
    const char *method_arg = method;
    ++method; // skip code

	//【11】分离方法和方法参数,获取方法名称和各个参数。
    QArgumentTypeArray methodTypes;
    QByteArray methodName = QmetaObjectPrivate::decodeMethodSignature(method, methodTypes);
    //【12】获取接受方的meta对象。即staticmetaObject对象和QObject::d_ptr->dynamicmetaObject()
    const QmetaObject *rmeta = receiver->metaObject();
    int method_index_relative = -1;
    Q_ASSERT(QmetaObjectPrivate::get(rmeta)->revision >= 7);
    //【13】根据方法类型,获取方法索引。
    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;
    }
    //【14】值判断,如果小于0,去除特殊符号,重新从本地信号中获取,然后信号的名称与参数分开、获取发送方的meteobject、 获取信号的索引。 
    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;
        }
    }
	//【15】方法的索引小于0,则提示错误信息,然后return。
    if (method_index_relative < 0) {
        err_method_notfound(receiver, method_arg, "connect");
        err_info_about_objects("connect", sender, receiver);
        return QmetaObject::Connection(0);
    }
	//【16】检测信号函数和槽函数的参数类型以及参数个数是否是一致的。
    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);
    }
	//【17】判断connect的连接类型的有效性。
    int *types = 0;
    if ((type == Qt::QueuedConnection)
            && !(types = queuedConnectionTypes(signalTypes.constData(), signalTypes.size()))) {
        return QmetaObject::Connection(0);
    }
	//【18】接收方和发送方的检查,具体检查啥,我也不知道。
#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
	//【19】准备创建信号槽结构体,然后添加到vector中。
    QmetaObject::Connection handle = QmetaObject::Connection(QmetaObjectPrivate::connect(
        sender, signal_index, smeta, receiver, method_index_relative, rmeta ,type, types));
    return handle;
}

【1】检测参数是否为空,如果为空,返回QmetaObject::Connection(0),类似return 0
【2】检查信号的有效性,实际上就是判断SIGNAL()展开后的得到的字符串的前面的标识是否为2。附上其中函数源码

static bool check_signal_macro(const QObject *sender, const char *signal,
                                const char *func, const char *op)
{
    int sigcode = extract_code(signal);
    if (sigcode != QSIGNAL_CODE) {
        if (sigcode == QSLOT_CODE)
            qWarning("QObject::%s: Attempt to %s non-signal %s::%s",
                     func, op, sender->metaObject()->className(), signal+1);
        else
            qWarning("QObject::%s: Use the SIGNAL macro to %s %s::%s",
                     func, op, sender->metaObject()->className(), signal);
        return false;
    }
    return true;
}

【3】获取发送方的meteobject,实际上就是上面讲到的staticmetaObject对象和QObject::d_ptr->dynamicmetaObject() 。
【4】将信号的名称与参数分开,获取到信号名称和各个参数,附上其中函数源码。

QByteArray QmetaObjectPrivate::decodeMethodSignature(
        const char *signature, QArgumentTypeArray &types)
{
    Q_ASSERT(signature != 0);
    const char *lparens = strchr(signature, '(');
    if (!lparens)
        return QByteArray();
    const char *rparens = strrchr(lparens + 1, ')');
    if (!rparens || *(rparens+1))
        return QByteArray();
    int nameLength = lparens - signature;
    argumentTypesFromString(lparens + 1, rparens, types);
    return QByteArray::fromRawData(signature, nameLength);
}

static void argumentTypesFromString(const char *str, const char *end,
                                    QArgumentTypeArray &types)
{
    Q_ASSERT(str <= end);
    while (str != end) {
        if (!types.isEmpty())
            ++str; // Skip comma
        const char *begin = str;
        int level = 0;
        while (str != end && (level > 0 || *str != ',')) {
            if (*str == '<')
                ++level;
            else if (*str == '>')
                --level;
            ++str;
        }
        types += QArgumentType(QByteArray(begin, str - begin));
    }
}

【5】获取信号的索引,附上其中函数源码。

int QmetaObjectPrivate::indexOfSignalRelative(const QmetaObject **baseObject,
                                              const QByteArray &name, int argc,
                                              const QArgumentType *types)
{
    int i = indexOfMethodRelative(baseObject, name, argc, types);
#ifndef QT_NO_DEBUG
    const QmetaObject *m = *baseObject;
    if (i >= 0 && m && m->d.superdata) {
        int conflict = indexOfMethod(m->d.superdata, name, argc, types);
        if (conflict >= 0) {
            QmetaMethod conflictMethod = m->d.superdata->method(conflict);
            qWarning("QmetaObject::indexOfSignal: signal %s from %s redefined in %s",
                     conflictMethod.methodSignature().constData(),
                     objectClassName(m->d.superdata), objectClassName(m));
        }
     }
 #endif
     return i;
}

template
static inline int indexOfMethodRelative(const QmetaObject **baseObject,
                                        const QByteArray &name, int argc,
                                        const QArgumentType *types)
{
    for (const QmetaObject *m = *baseObject; m; m = m->d.superdata) {
        Q_ASSERT(priv(m->d.data)->revision >= 7);
        int i = (MethodType == MethodSignal)
                 ? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1);
        const int end = (MethodType == MethodSlot)
                        ? (priv(m->d.data)->signalCount) : 0;

        for (; i >= end; --i) {
            int handle = priv(m->d.data)->methodData + 5*i;
            if (methodMatch(m, handle, name, argc, types)) {
                *baseObject = m;
                return i;
            }
        }
    }
    return -1;
}

【6】信号索引值判断,如果小于0,去除特殊符号,重新从本地信号中获取,然后信号的名称与参数分开、获取发送方的meteobject、 获取信号的索引。附上其中函数源码。

QByteArray QmetaObject::normalizedSignature(const char *method)
{
    QByteArray result;
    if (!method || !*method)
        return result;
    int len = int(strlen(method));
    QVarLengthArray stackbuf(len + 1);
    char *d = stackbuf.data();
    qRemoveWhitespace(method, d);

    result.reserve(len);

    int argdepth = 0;
    int templdepth = 0;
    while (*d) {
        if (argdepth == 1) {
            d = qNormalizeType(d, templdepth, result);
            if (!*d) //most likely an invalid signature.
                break;
        }
        if (*d == '(')
            ++argdepth;
        if (*d == ')')
            --argdepth;
        result += *d++;
    }

    return result;
}

【7】信号的索引小于0,则提示错误信息,然后return。
【8】这步具体我也不是很理解,大概意思是将索引重新计算了一次。附上其中函数源码

int QmetaObjectPrivate::originalClone(const QmetaObject *mobj, int local_method_index)
{
    Q_ASSERT(local_method_index < get(mobj)->methodCount);
    int handle = get(mobj)->methodData + 5 * local_method_index;
    while (mobj->d.data[handle + 4] & MethodCloned) {
        Q_ASSERT(local_method_index > 0);
        handle -= 5;
        local_method_index--;
    }
    return local_method_index;
}
int QmetaObjectPrivate::signalOffset(const QmetaObject *m)
{
    Q_ASSERT(m != 0);
    int offset = 0;
    for (m = m->d.superdata; m; m = m->d.superdata)
        offset += priv(m->d.data)->signalCount;
    return offset;
}

【9】获取方法类型,即接收方是槽函数还是信号转发,附上其中函数源码

static int extract_code(const char *member)
{
    // extract code, ensure QMETHOD_CODE <= code <= QSIGNAL_CODE
    return (((int)(*member) - '0') & 0x3);
}

【10】检测方法有效性。附上其中函数源码

static bool check_method_code(int code, const QObject *object,
                               const char *method, const char *func)
{
    if (code != QSLOT_CODE && code != QSIGNAL_CODE) {
        qWarning("QObject::%s: Use the SLOT or SIGNAL macro to "
                 "%s %s::%s", func, func, object->metaObject()->className(), method);
        return false;
    }
    return true;
}

【11】分离方法和方法参数,获取方法名称和各个参数。
【12】获取接受方的meta对象。即staticmetaObject对象和QObject::d_ptr->dynamicmetaObject()。
【13】根据方法类型,获取方法索引。附上其中函数源码

int QmetaObjectPrivate::indexOfSlotRelative(const QmetaObject **m,
                                            const QByteArray &name, int argc,
                                            const QArgumentType *types)
{
    return indexOfMethodRelative(m, name, argc, types);
}

【14】值判断,如果小于0,去除特殊符号,重新从本地信号中获取,然后信号的名称与参数分开、获取发送方的meteobject、 获取信号的索引。
【15】方法的索引小于0,则提示错误信息,然后return。
【16】检测信号函数和槽函数的参数类型以及参数个数是否是一致的。附上其中函数源码

bool QmetaObjectPrivate::checkConnectArgs(const QmetaMethodPrivate *signal,
                                          const QmetaMethodPrivate *method)
{
    if (signal->methodType() != QmetaMethod::Signal)
        return false;
    if (signal->parameterCount() < method->parameterCount())
        return false;
    const QmetaObject *smeta = signal->enclosingmetaObject();
    const QmetaObject *rmeta = method->enclosingmetaObject();
    for (int i = 0; i < method->parameterCount(); ++i) {
        uint sourceTypeInfo = signal->parameterTypeInfo(i);
        uint targetTypeInfo = method->parameterTypeInfo(i);
        if ((sourceTypeInfo & IsUnresolvedType)
            || (targetTypeInfo & IsUnresolvedType)) {
            QByteArray sourceName = typeNameFromTypeInfo(smeta, sourceTypeInfo);
            QByteArray targetName = typeNameFromTypeInfo(rmeta, targetTypeInfo);
            if (sourceName != targetName)
                return false;
        } else {
            int sourceType = typeFromTypeInfo(smeta, sourceTypeInfo);
            int targetType = typeFromTypeInfo(rmeta, targetTypeInfo);
            if (sourceType != targetType)
                return false;
        }
    }
    return true;
}

【17】判断connect的连接类型的有效性。附上其中函数源码

static int *queuedConnectionTypes(const QArgumentType *argumentTypes, int argc)
{
    QScopedArrayPointer types(new int [argc + 1]);
    for (int i = 0; i < argc; ++i) {
        const QArgumentType &type = argumentTypes[i];
        if (type.type())
            types[i] = type.type();
        else if (type.name().endsWith('*'))
            types[i] = QmetaType::VoidStar;
        else
            types[i] = QmetaType::type(type.name());

        if (!types[i]) {
            qWarning("QObject::connect: Cannot queue arguments of type '%s'n"
                     "(Make sure '%s' is registered using qRegistermetaType().)",
                     type.name().constData(), type.name().constData());
            return 0;
        }
    }
    types[argc] = 0;

    return types.take();
}

【18】接收方和发送方的检查,具体检查啥,我也不知道。附上其中函数源码

QmetaMethod QmetaObjectPrivate::signal(const QmetaObject *m, int signal_index)
{
    QmetaMethod result;
    if (signal_index < 0)
        return result;
    Q_ASSERT(m != 0);
    int i = signal_index;
    i -= signalOffset(m);
    if (i < 0 && m->d.superdata)
        return signal(m->d.superdata, signal_index);

    if (i >= 0 && i < priv(m->d.data)->signalCount) {
        result.mobj = m;
        result.handle = priv(m->d.data)->methodData + 5*i;
    }
    return result;
}
QmetaMethod QmetaObject::method(int index) const
{
    int i = index;
    i -= methodOffset();
    if (i < 0 && d.superdata)
        return d.superdata->method(index);

    QmetaMethod result;
    if (i >= 0 && i < priv(d.data)->methodCount) {
        result.mobj = this;
        result.handle = priv(d.data)->methodData + 5*i;
    }
    return result;
}
#ifndef QT_NO_DEBUG
static inline void check_and_warn_compat(const QmetaObject *sender, const QmetaMethod &signal,
                                         const QmetaObject *receiver, const QmetaMethod &method)
{
    if (signal.attributes() & QmetaMethod::Compatibility) {
        if (!(method.attributes() & QmetaMethod::Compatibility))
            qWarning("QObject::connect: Connecting from COMPAT signal (%s::%s)",
                     sender->className(), signal.methodSignature().constData());
    } else if ((method.attributes() & QmetaMethod::Compatibility) &&
               method.methodType() == QmetaMethod::Signal) {
        qWarning("QObject::connect: Connecting from %s::%s to COMPAT slot (%s::%s)",
                 sender->className(), signal.methodSignature().constData(),
                 receiver->className(), method.methodSignature().constData());
    }
}
#endif

【19】准备创建信号槽结构体,然后添加到vector中。我们进入它的源码中来看看

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)
{
    QObject *s = const_cast(sender);
    QObject *r = const_cast(receiver);

    int method_offset = rmeta ? rmeta->methodOffset() : 0;
    Q_ASSERT(!rmeta || QmetaObjectPrivate::get(rmeta)->revision >= 6);
    QObjectPrivate::StaticmetaCallFunction callFunction =
        rmeta ? rmeta->d.static_metacall : 0;

    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;

            int method_index_absolute = method_index + method_offset;

            while (c2) {
                if (!c2->isSlotObject && c2->receiver == receiver && c2->method() == method_index_absolute)
                    return 0;
                c2 = c2->nextConnectionList;
            }
        }
        type &= Qt::UniqueConnection - 1;
    }

    QScopedPointer c(new QObjectPrivate::Connection);
    c->sender = s;
    c->signal_index = signal_index;
    c->receiver = r;
    c->method_relative = method_index;
    c->method_offset = method_offset;
    c->connectionType = type;
    c->isSlotObject = false;
    c->argumentTypes.store(types);
    c->nextConnectionList = 0;
    c->callFunction = callFunction;

    QObjectPrivate::get(s)->addConnection(signal_index, c.data());

    locker.unlock();
    QmetaMethod smethod = QmetaObjectPrivate::signal(smeta, signal_index);
    if (smethod.isValid())
        s->connectNotify(smethod);

    return c.take();
}

struct ConnectionList {
        ConnectionList() : first(nullptr), last(nullptr) {}
        Connection *first;
        Connection *last;
    };
    
void QObjectPrivate::addConnection(int signal, Connection *c)
{
    Q_ASSERT(c->sender == q_ptr);
    if (!connectionLists)
        connectionLists = new QObjectConnectionListVector();
    if (signal >= connectionLists->count())
        connectionLists->resize(signal + 1);

    ConnectionList &connectionList = (*connectionLists)[signal];
    if (connectionList.last) {
        connectionList.last->nextConnectionList = c;
    } else {
        connectionList.first = c;
    }
    connectionList.last = c;

    cleanConnectionLists();

    c->prev = &(QObjectPrivate::get(c->receiver)->senders);
    c->next = *c->prev;
    *c->prev = c;
    if (c->next)
        c->next->prev = &c->next;

    if (signal < 0) {
        connectedSignals[0].store(~0);
        connectedSignals[1].store(~0);
    } else if (signal < (int)sizeof(connectedSignals) * 8) {
        connectedSignals[signal >> 5].store(connectedSignals[signal >> 5].load() | (1 << (signal & 0x1f)));
    }
}

从以上代码来看,就是创建了一个ConnectionList的结构体,存放接收方对象,发送方对象,接收方meta对象,发送方meta对象,信号索引,方法索引,接收方这边的回调等成员,然后将这个结构体存放到了QObjectConnectionListVector中。

第三站,connect的源码我们了解了,现在,来看为什么发送信号后槽函数就执行了,这个地方我们就得看信号的函数体了,
void Counter::valueChanged(int _t1)
{
    void *_a[] = { Q_NULLPTR, const_cast(reinterpret_cast(&_t1)) };
    QmetaObject::activate(this, &staticmetaObject, 0, _a);
}

这里面执行了activate,那activate里面怎么做的呢。

void QmetaObject::activate(QObject *sender, int signalOffset, int local_signal_index, void **argv)
{
	// 获取信号索引
    int signal_index = signalOffset + local_signal_index;

    if (sender->d_func()->blockSig)
        return;

    Q_TRACE_SCOPE(QmetaObject_activate, sender, signal_index);

    if (sender->d_func()->isDeclarativeSignalConnected(signal_index)
            && QAbstractDeclarativeData::signalEmitted) {
        Q_TRACE_SCOPE(QmetaObject_activate_declarative_signal, sender, signal_index);
        QAbstractDeclarativeData::signalEmitted(sender->d_func()->declarativeData, sender,
                                                signal_index, argv);
    }

    if (!sender->d_func()->isSignalConnected(signal_index,  false)
        && !qt_signal_spy_callback_set.signal_begin_callback
        && !qt_signal_spy_callback_set.signal_end_callback) {
        // The possible declarative connection is done, and nothing else is connected, so:
        return;
    }

    void *empty_argv[] = { 0 };
    if (qt_signal_spy_callback_set.signal_begin_callback != 0) {
        qt_signal_spy_callback_set.signal_begin_callback(sender, signal_index,
                                                         argv ? argv : empty_argv);
    }

    {
    QMutexLocker locker(signalSlotLock(sender));
    struct ConnectionListsRef {
        QObjectConnectionListVector *connectionLists;
        ConnectionListsRef(QObjectConnectionListVector *connectionLists) : connectionLists(connectionLists)
        {
            if (connectionLists)
                ++connectionLists->inUse;
        }
        ~ConnectionListsRef()
        {
            if (!connectionLists)
                return;

            --connectionLists->inUse;
            Q_ASSERT(connectionLists->inUse >= 0);
            if (connectionLists->orphaned) {
                if (!connectionLists->inUse)
                    delete connectionLists;
            }
        }

        QObjectConnectionListVector *operator->() const { return connectionLists; }
    };
    //从这里开始才是正式的槽函数处理,它获取了QObjectConnectionListVector这个存放信号槽的容器
    ConnectionListsRef connectionLists = sender->d_func()->connectionLists;
    if (!connectionLists.connectionLists) {
        locker.unlock();
        if (qt_signal_spy_callback_set.signal_end_callback != 0)
            qt_signal_spy_callback_set.signal_end_callback(sender, signal_index);
        return;
    }
	//按照信号索引从QObjectConnectionListVector中找到ConnectionList结构体。
    const QObjectPrivate::ConnectionList *list;
    if (signal_index < connectionLists->count())
        list = &connectionLists->at(signal_index);
    else
        list = &connectionLists->allsignals;

    Qt::HANDLE currentThreadId = QThread::currentThreadId();

    do {
        QObjectPrivate::Connection *c = list->first;
        if (!c) continue;
        // We need to check against last here to ensure that signals added
        // during the signal emission are not emitted in this emission.
        QObjectPrivate::Connection *last = list->last;

        do {
            if (!c->receiver)
                continue;

            QObject * const receiver = c->receiver;
            const bool receiverInSameThread = currentThreadId == receiver->d_func()->threadData->threadId.load();

            // 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 ? argv : empty_argv, locker);
                continue;
#if QT_CONFIG(thread)
            } else if (c->connectionType == Qt::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;
                QmetaCallEvent *ev = c->isSlotObject ?
                    new QmetaCallEvent(c->slotObj, sender, signal_index, 0, 0, argv ? argv : empty_argv, &semaphore) :
                    new QmetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal_index, 0, 0, argv ? argv : empty_argv, &semaphore);
                QCoreApplication::postEvent(receiver, ev);
                locker.unlock();
                semaphore.acquire();
                locker.relock();
                continue;
#endif
            }

            QConnectionSenderSwitcher sw;

            if (receiverInSameThread) {
                sw.switchSender(receiver, sender, signal_index);
            }
            if (c->isSlotObject) {
                c->slotObj->ref();
                QScopedPointer obj(c->slotObj);
                locker.unlock();

                {
                    Q_TRACE_SCOPE(QmetaObject_activate_slot_functor, obj.data());
                    obj->call(receiver, argv ? argv : empty_argv);
                }

                // Make sure the slot object gets destroyed before the mutex is locked again, as the
                // destructor of the slot object might also lock a mutex from the signalSlotLock() mutex pool,
                // and that would deadlock if the pool happens to return the same mutex.
                obj.reset();

                locker.relock();
            } 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 methodIndex = c->method();
                const int method_relative = c->method_relative;
                //这个地方从结构体中取出了槽的回调函数
                const auto callFunction = c->callFunction;
                locker.unlock();
                if (qt_signal_spy_callback_set.slot_begin_callback != 0)
                    qt_signal_spy_callback_set.slot_begin_callback(receiver, methodIndex, argv ? argv : empty_argv);

                {
                    Q_TRACE_SCOPE(QmetaObject_activate_slot, receiver, methodIndex);
                    //执行回调函数
                    callFunction(receiver, QmetaObject::InvokemetaMethod, method_relative, argv ? argv : empty_argv);
                }

                if (qt_signal_spy_callback_set.slot_end_callback != 0)
                    qt_signal_spy_callback_set.slot_end_callback(receiver, methodIndex);
                locker.relock();
            } else {
                const int method = c->method_relative + c->method_offset;
                locker.unlock();

                if (qt_signal_spy_callback_set.slot_begin_callback != 0) {
                    qt_signal_spy_callback_set.slot_begin_callback(receiver,
                                                                method,
                                                                argv ? argv : empty_argv);
                }

                {
                    Q_TRACE_SCOPE(QmetaObject_activate_slot, receiver, method);
                    metacall(receiver, QmetaObject::InvokemetaMethod, method, argv ? argv : empty_argv);
                }

                if (qt_signal_spy_callback_set.slot_end_callback != 0)
                    qt_signal_spy_callback_set.slot_end_callback(receiver, method);

                locker.relock();
            }

            if (connectionLists->orphaned)
                break;
        } while (c != last && (c = c->nextConnectionList) != 0);

        if (connectionLists->orphaned)
            break;
    } while (list != &connectionLists->allsignals &&
        //start over for all signals;
        ((list = &connectionLists->allsignals), true));

    }

    if (qt_signal_spy_callback_set.signal_end_callback != 0)
        qt_signal_spy_callback_set.signal_end_callback(sender, signal_index);
}

从以上源码可以看出:
1、获取信号的索引值。
2、从sender->d_func()->connectionLists获取了QObjectConnectionListVector这个存放信号槽的容器。
3、按照信号索引从QObjectConnectionListVector中找到ConnectionList结构体。
4、从找到的结构体中取出了槽的回调函数。
5、执行回调函数。
整个信号与槽的过程就是这样来实现的。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/zaji/5593700.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-12-15
下一篇 2022-12-15

发表评论

登录后才能评论

评论列表(0条)

保存