Qt核心机制之信号槽

前言

前面讲了Qt module小模块介绍, 后续将会接着小模块讲解Qt核心机制相关,Qt核心主要涉及到信号槽, 属性机制, 事件模型, 元对象等。这篇博客主要核心功能机制–信号槽进行详解。

信号槽

信号槽源码分析 这篇博客还是比较详细, 可以参考。

众所周知, 信号槽是我们在Qt中常用的一种通信机制(同步, 异步都可)。
我们常常通过connect去连接信号槽, 具体形式可参考Qt connect几种写法与连接方式

我们都知道当我们声明一个直接或者间接继承自QObject类后,

class MyObject: public QObject
{
	Q_OBJECT  //声明
    Q_SIGNALS://信号
    void sigBtnClicked(int);
    public Q_SLOTS://}

先看一下 Q_OBJECT展开

/* qmake ignore Q_OBJECT */
#define Q_OBJECT \
public: \
    Q_OBJECT_CHECK \
    QT_WARNING_PUSH \
    Q_OBJECT_NO_OVERRIDE_WARNING \
    static const QMetaObject staticMetaObject; \
    virtual const QMetaObject *metaObject() const; \
    virtual void *qt_metacast(const char *); \
    virtual int qt_metacall(QMetaObject::Call, int, void **); \
    QT_TR_FUNCTIONS \
private: \
    Q_OBJECT_NO_ATTRIBUTES_WARNING \
    Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
    QT_WARNING_POP \
    struct QPrivateSignal {}; \
    QT_ANNOTATE_CLASS(qt_qobject, "")

先暂时知道这些函数, 然后编译器在编译后, 你会发现多生成了 moc_MyObject.cpp 文件. 稍微细心, 你会发现这有对Q_OBJECT展开的函数进行定义实现., 具体我并未写那么多, 把核心函数写下来.

QT_BEGIN_MOC_NAMESPACE
void MyObject::qt_static_metacall(QObject* _o, QMetaObject::call _c, int _id, void **a)
{
  if (_c == QMetaObject::InvokeMetaMethod) {
        Q_ASSERT(staticMetaObject.cast(_o));
        MainWindow *_t = static_cast<MainWindow *>(_o);
        Q_UNUSED(_t)
        switch (_id) {
        case 0: _t->sigBtnClicked((*reinterpret_cast< int(*)>(_a[1]))); break;
        default: ;
        }
    } 
    else if (_c == QMetaObject::IndexOfMethod) {
        int *result = reinterpret_cast<int *>(_a[0]);
        void **func = reinterpret_cast<void **>(_a[1]);
        {
            typedef void (MainWindow::*_t)(int );
            if (*reinterpret_cast<_t *>(func) == static_cast<_t>(&MainWindow::sigBtnClicked)) {
                *result = 0;
                return;
            }
        }
    }
}
...

// SIGNAL 0
void MyObject::sigBtnClicked(int _t1)
{
    void *_a[] = { Q_NULLPTR, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 0, _a);
}

QT_END_MOC_NAMESPACE

我们知道Qt中 信号是不用我们 实现的, 那是因为在上式文件中Qt在编译过程文件已经实现了. 当我们发送emit sigBtnClicked(); 时, 其实就是调用moc_MyObject.cpp 中sigBtnClicked() 这个函数.

接下来, 我们顺藤摸瓜, 看看 sigBtnClicked() 函数. 它里面调用了 QMetaObject::activate;
QMetaObject::activate
主要我们就会发现
触发信号 => QMetaObject::activate => 获取信号发送者connectionLists,找到信号对应的的ConnectionList 然后循环遍历该连接表,单个 Connection => 如果同一个线程直接调用接收者的qt_metaCall 最终函数回调调用了qt_static_metacall, 如果不是在同一线程, 会向接收者所在线程队列post QMetaCallEvent事件, 等接受者线程轮循到该事件时, 在处理,最后最终函数回调调用了qt_static_metacall。

如果是异步的话, 接收者与发送者不在同一个线程之内, queued_activate()

static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
{
    if (!c->argumentTypes && c->argumentTypes != &DIRECT_CONNECTION_ONLY) {
        QMetaMethod m = sender->metaObject()->method(signal);
        int *tmp = queuedConnectionTypes(m.parameterTypes());
        if (!tmp) // cannot queue arguments
            tmp = &DIRECT_CONNECTION_ONLY;
        if (!c->argumentTypes.testAndSetOrdered(0, tmp)) {
            if (tmp != &DIRECT_CONNECTION_ONLY)
                delete [] tmp;
        }
    }
    if (c->argumentTypes == &DIRECT_CONNECTION_ONLY) // cannot activate
        return;
    int nargs = 1; // include return type
    while (c->argumentTypes[nargs-1])
        ++nargs;
    int *types = (int *) qMalloc(nargs*sizeof(int));
    Q_CHECK_PTR(types);
    void **args = (void **) qMalloc(nargs*sizeof(void *));
    Q_CHECK_PTR(args);
    types[0] = 0; // return type
    args[0] = 0; // return value
    for (int n = 1; n < nargs; ++n)
        args[n] = QMetaType::construct((types[n] = c->argumentTypes[n-1]), argv[n]);
    QCoreApplication::postEvent(c->receiver, new QMetaCallEvent(c->method_offset,
                                                                c->method_relative,
                                                                c->callFunction,
                                                                sender, signal, nargs,
                                                                types, args));
}

Connections

上一节说到, 信号的发送方会有对应链接列表,

如图方便更好的理解连接列表。
在这里插入图片描述
每个ConnectionList类型元素是一个双向链表,保存了信号的所有连接。对于每一个连接的类型Connection结构如下:

struct Connection
{
    QObject *sender;//发送者
    QObject *receiver;//接受者
    StaticMetaCallFunction callFunction;//调用的槽函数
    // The next pointer for the singly-linked ConnectionList
    Connection *nextConnectionList;
    //senders linked list
    Connection *next;
    Connection **prev;
    QBasicAtomicPointer<int> argumentTypes;
    ushort method_offset;
    ushort method_relative;
    ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
    ~Connection();
    int method() const { return method_offset + method_relative; }
};

里面包含了发送者, 接收者,下一个连接, 连接类型, 以及调用槽函数StaticMetaCallFunction callFunction等等。
而StaticMetaCallFunction 作为一个函数指针, 存在QObject对象的私有数据QObjectPrivate:

class Q_CORE_EXPORT QObjectPrivate : public QObjectData
{
    Q_DECLARE_PUBLIC(QObject)
public:
    struct ExtraData
    {
        ExtraData() {}
        QList<QByteArray> propertyNames;
        QList<QVariant> propertyValues;
    };

    typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);
    struct Connection
    {
        QObject *sender;
        QObject *receiver;
        StaticMetaCallFunction callFunction;
        // The next pointer for the singly-linked ConnectionList
        Connection *nextConnectionList;
        //senders linked list
        Connection *next;
        Connection **prev;
        QBasicAtomicPointer<int> argumentTypes;
        ushort method_offset;
        ushort method_relative;
        ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
        ~Connection();
        int method() const { return method_offset + method_relative; }
    };
    // ConnectionList is a singly-linked list
    struct ConnectionList {
        ConnectionList() : first(0), last(0) {}
        Connection *first;
        Connection *last;
    };

    struct Sender
    {
        QObject *sender;
        int signal;
        int ref;
    };

    QObjectPrivate(int version = QObjectPrivateVersion);
    virtual ~QObjectPrivate();
    void deleteChildren();

    void setParent_helper(QObject *);
    void moveToThread_helper();
    void setThreadData_helper(QThreadData *currentData, QThreadData *targetData);
    void _q_reregisterTimers(void *pointer);

    bool isSender(const QObject *receiver, const char *signal) const;
    QObjectList receiverList(const char *signal) const;
    QObjectList senderList() const;

    void addConnection(int signal, Connection *c);
    void cleanConnectionLists();

    static inline Sender *setCurrentSender(QObject *receiver,
                                    Sender *sender);
    static inline void resetCurrentSender(QObject *receiver,
                                   Sender *currentSender,
                                   Sender *previousSender);
    static void clearGuards(QObject *);

    static QObjectPrivate *get(QObject *o) {
        return o->d_func();
    }

    int signalIndex(const char *signalName) const;
    inline bool isSignalConnected(uint signalIdx) const;

    // To allow arbitrary objects to call connectNotify()/disconnectNotify() without making
    // the API public in QObject. This is used by QDeclarativeNotifierEndpoint.
    inline void connectNotify(const char *signal);
    inline void disconnectNotify(const char *signal);

    static inline void signalSignature(const QMetaMethod &signal,
                                       QVarLengthArray<char> *result);

public:
    QString objectName;
    ExtraData *extraData;    // extra data set by the user
    QThreadData *threadData; // id of the thread that owns the object

    QObjectConnectionListVector *connectionLists;//连接链表向量容器

    Connection *senders;     // linked list of connections connected to this object
    Sender *currentSender;   // object currently activating the object
    mutable quint32 connectedSignals[2];

    // preserve binary compatibility with code compiled without Qt 3 support
    // keeping the binary layout stable helps the Qt Creator debugger
    void *unused;

    QList<QPointer<QObject> > eventFilters;
    union {
        QObject *currentChildBeingDeleted;
        QAbstractDeclarativeData *declarativeData; //extra data used by the declarative module
    };

    // these objects are all used to indicate that a QObject was deleted
    // plus QPointer, which keeps a separate list
    QAtomicPointer<QtSharedPointer::ExternalRefCountData> sharedRefcount;
};

函数回调

 typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **); //是qt_static_metacall函数指针

connect是啥

结果

不管所显示的 StaticMetaCallFunction callFunction, 以及qt_metaCall等最终还是会调用的 qt_static_metacall 去调用函数。
最后结果来说: 就是函数回调。 只不过可能是需要查询其信号所发送者connectionLists, connection处理花费一定性能。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

道阻且长,行则降至

无聊,打赏求刺激而已

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值