zoukankan      html  css  js  c++  java
  • 7、Qt MetaObject System详解

    网上的资源比较乱,该文章整理自地址:http://www.xuebuyuan.com/735789.html

    Qt meta-object系统基于三个方面:

     1、QObject提供一个基类,方便派生类使用meta-object系统的功能;

     2、Q_OBJECT宏,在类的声明体内激活meta-object功能,比如动态属性、信号、槽;

     3、Meta Object编译器(MOC),为每个QObject派生类生成代码,以支持meta-object功能

    QObject定义了从一个QObject对象访问meta-object功能的接口,Q_OBJECT宏用来告诉编译器该类需要激活meta-object功能,编译器在扫描一个源文件时,如果发现类的声明中有这个宏,就会生成一些代码来为支持meta-object功能——主要是生成该类对应MetaObject类以及对QObject的函数override。

    QObject和QMetaObject:

    顾名思义,QMetaObject包含了QObject的所谓的元数据,也就是QObject信息的一些描述信息:除了类型信息外,还包含QT中特有的signal&slot信息。

    1 QObject::metaObject()

    该方法返回一个QObject对象对应的metaobject对象,注意这个方法是virtual方法。如上文所说,如果一个类的声明中包含了Q_OBJECT宏,编译器会生成代码来实现这个类对应的QMetaObject类,并重载QObject::metaObject()方法来返回这个QMetaObject类的实例引用。这样当通过QObject类型的引用调用metaObejct方法时,返回的是这个引用的所指的真实对象的metaobject。

    如果一个类从QObject派生,确没有声明Q_OBJECT宏,那么这个类的metaobject对象不会被生成,这样这个类所声明的signal slot都不能使用,而这个类实例调用metaObject()返回的就是其父类的metaobject对象,这样导致的后果就是你从这个类实例获得的元数据其实都是父类的数据,这显然给你的代码埋下隐患。因此如果一个类从QOBject派生,它都应该声明Q_OBJECT宏,不管这个类有没有定义signal&slot和Property。

    这样每个QObject类都有一个对应的QMetaObject类,形成一个平行的类型层次。

    QMetaObject提供的信息:

    下面通过QMetaObject的接口来解读QMetaObject提供的信息:

    1,基本信息

    1 struct Q_CORE_EXPORT QMetaObject
    2 {
    3     const char *className() const;
    4     const QMetaObject *superClass() const;

    2,classinfo:提供额外的类信息。其实就是一些名值对。用户可以在类的声明中以

    1 Q_CLASSINFO(name, value)

    方式添加

    1     int classInfoOffset() const;
    2     int classInfoCount() const;
    3     int indexOfClassInfo(const char *name) const;
    4     QMetaClassInfo classInfo(int index) const;

    3、contructor:提供该类的构造方法信息

    1     int constructorCount() const;
    2     int indexOfConstructor(const char *constructor) const;
    3     QMetaMethod constructor(int index) const;

    4、enum:描述该类声明体中所包含的枚举类型信息

    1     int enumeratorOffset() const;
    2     int enumeratorCount() const;
    3     int indexOfEnumerator(const char *name) const;
    4     QMetaEnum enumerator(int index) const;

    5、method:描述类中所包含方法信息:包括property,signal,slot等,包括祖先类,如何组织暂时不确定。

    1     int methodOffset() const;
    2     int methodCount() const;
    3     int indexOfMethod(const char *method) const;
    4     int indexOfSignal(const char *signal) const;
    5     int indexOfSlot(const char *slot) const;
    6     QMetaMethod method(int index) const;

    6、property:类型的属性信息

    1     int propertyOffset() const;
    2     int propertyCount() const;
    3     int indexOfProperty(const char *name) const;
    4     QMetaProperty property(int index) const; ////返回类中设置了USERflag的属性,(难道只能有一个这样的属性?)

    注意:对于类里面定义的函数,构造函数,枚举,只有加上一些宏才表示你希望为方法提供meta信息。比如 Q_ENUMS用来注册宏,

    Q_INVACABLE用来注册方法(包括构造函数)。Qt这么设计的原因应该是避免meta信息的臃肿。

    下文来源: http://biancheng.dnbcw.info/linux/253557.html

    如果一个类的声明中包含Q_OBJECT宏,那么qmake将为这个类生成meta信息,这个信息在前一篇中所提到的moc文件中。这一篇通过解析这个一个示例moc文件来阐述这些meta信息的存储方式和格式;本篇先说明了一下QMetaObject的数据结构,然后呈现了一个简单的类TestObject类及其生成的moc文件,最后对这个moc文件个内容进行了详细解释。

    QMetaObject的数据定义:

    QMetaObject包含唯一的数据成员如下(见头文件qobjectdefs.h)

    复制代码
    1 struct Q_CORE_EXPORT QMetaObject
    2 {
    3     struct { // private data
    4         const QMetaObject *superdata; //父类QMetaObject实例的指针
    5         const char *stringdata;  //一段字符串内存块,包含MetaObject信息之字符串信息
    6         const uint *data;  //一段二级制内存块,包含MetaObject信息之二进制信息
    7         const void *extradata; //额外字段,暂未使用
    8     } d;
    9 };
    复制代码

    QMetaObjectPrivate的数据定义:

    QMetaObjectPrivate是QMetaObject的私有实现类,其数据定义部分如下(见头文件qmetaobject_p.h)。该数据结构全是int类型,一些是直接的int型信息,比如classInfoCount、
    methodCount等,还有一些是用于在QMetaObject的stringdata和data内存块中定位信息的索引值。下文结合这两个内存块的结构再分析个字段的含义。

    复制代码
     1 struct QMetaObjectPrivate
     2 {
     3     int revision;
     4     int className;
     5     int classInfoCount, classInfoData;
     6     int methodCount, methodData;
     7     int propertyCount, propertyData;
     8     int enumeratorCount, enumeratorData;
     9     int constructorCount, constructorData; //since revision 2
    10     int flags; //since revision 3
    11     int signalCount; //since revision 4
    12     // revision 5 introduces changes in normalized signatures, no new members
    13     // revision 6 added qt_static_metacall as a member of each Q_OBJECT and inside QMetaObject itself
    14 };
    复制代码

    下文利用一个示例QObject子类及其moc文件,来分析QMetaObject的信息结构。

    示例类TestObject:

    TestObject类继承自QObject,定义了两个Property:propertyA,propertyB;两个classinfo:Author,Version;一个枚举:TestEnum。

    复制代码
     1     #include <QObject>  
     2     class TestObject : public QObject  
     3     {  
     4         Q_OBJECT  
     5         Q_PROPERTY(QString propertyA  READ getPropertyA WRITE getPropertyA RESET resetPropertyA DESIGNABLE true SCRIPTABLE true STORED true USER false)  
     6         Q_PROPERTY(QString propertyB  READ getPropertyB WRITE getPropertyB RESET resetPropertyB)  
     7         Q_CLASSINFO("Author", "Long Huihu")  
     8         Q_CLASSINFO("Version", "TestObjectV1.0")  
     9         Q_ENUMS(TestEnum)  
    10     public:  
    11         enum TestEnum {  
    12             EnumValueA,  
    13             EnumValueB  
    14         };  
    15     public:  
    16         TestObject();  
    17     signals:  
    18         void clicked();  
    19         void pressed();  
    20     public slots:  
    21         void onEventA(const QString &);  
    22         void onEventB(int );  
    23     }  
    复制代码

    示例类TestObject的moc文件:

    复制代码
      1     #include "TestObject.h"  
      2     #if !defined(Q_MOC_OUTPUT_REVISION)  
      3     #error "The header file 'TestObject.h' doesn't include <QObject>."  
      4     #elif Q_MOC_OUTPUT_REVISION != 62  
      5     #error "This file was generated using the moc from 4.6.0. It"  
      6     #error "cannot be used with the include files from this version of Qt."  
      7     #error "(The moc has changed too much.)"  
      8     #endif  
      9     QT_BEGIN_MOC_NAMESPACE  
     10     static const uint qt_meta_data_TestObject[] = {  
     11      // content:  
     12            4,       // revision  
     13            0,       // classname  
     14            2,   14, // classinfo  
     15            4,   18, // methods  
     16            2,   38, // properties  
     17            1,   44, // enums/sets  
     18            0,    0, // constructors  
     19            0,       // flags  
     20            2,       // signalCount  
     21      // classinfo: key, value  
     22           22,   11,  
     23           44,   29,  
     24      // signals: signature, parameters, type, tag, flags  
     25           53,   52,   52,   52, 0x05,  
     26           63,   52,   52,   52, 0x05,  
     27      // slots: signature, parameters, type, tag, flags  
     28           73,   52,   52,   52, 0x0a,  
     29           91,   52,   52,   52, 0x0a,  
     30      // properties: name, type, flags  
     31          113,  105, 0x0a095007,  
     32          123,  105, 0x0a095007,  
     33      // enums: name, flags, count, data  
     34          133, 0x0,    2,   48,  
     35      // enum data: key, value  
     36          142, uint(TestObject::EnumValueA),  
     37          153, uint(TestObject::EnumValueB),  
     38            0        // eod  
     39     };  
     40     static const char qt_meta_stringdata_TestObject[] = {  
     41         "TestObjectLong HuihuAuthor"  
     42         "TestObjectV1.0Versionclicked()"  
     43         "pressed()onEventA(QString)onEventB(int)"  
     44         "QStringpropertyApropertyBTestEnum"  
     45         "EnumValueAEnumValueB"  
     46     };  
     47     const QMetaObject TestObject::staticMetaObject = {  
     48         { &QObject::staticMetaObject, qt_meta_stringdata_TestObject,  
     49           qt_meta_data_TestObject, 0 }  
     50     };  
     51     #ifdef Q_NO_DATA_RELOCATION  
     52     const QMetaObject &TestObject::getStaticMetaObject() { return staticMetaObject; }  
     53     #endif //Q_NO_DATA_RELOCATION  
     54     const QMetaObject *TestObject::metaObject() const  
     55     {  
     56         return QObject::d_ptr->metaObject ? QObject::d_ptr->metaObject : &staticMetaObject;  
     57     }  
     58     void *TestObject::qt_metacast(const char *_clname)  
     59     {  
     60         if (!_clname) return 0;  
     61         if (!strcmp(_clname, qt_meta_stringdata_TestObject))  
     62             return static_cast<void*>(const_cast< TestObject*>(this));  
     63         return QObject::qt_metacast(_clname);  
     64     }  
     65     int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)  
     66     {  
     67         _id = QObject::qt_metacall(_c, _id, _a);  
     68         if (_id < 0)  
     69             return _id;  
     70         if (_c == QMetaObject::InvokeMetaMethod) {  
     71             switch (_id) {  
     72             case 0: clicked(); break;  
     73             case 1: pressed(); break;  
     74             case 2: onEventA((*reinterpret_cast< const QString(*)>(_a[1]))); break;  
     75             case 3: onEventB((*reinterpret_cast< int(*)>(_a[1]))); break;  
     76             default: ;  
     77             }  
     78             _id -= 4;  
     79         }  
     80     #ifndef QT_NO_PROPERTIES  
     81           else if (_c == QMetaObject::ReadProperty) {  
     82             void *_v = _a[0];  
     83             switch (_id) {  
     84             case 0: *reinterpret_cast< QString*>(_v) = getPropertyA(); break;  
     85             case 1: *reinterpret_cast< QString*>(_v) = getPropertyB(); break;  
     86             }  
     87             _id -= 2;  
     88         } else if (_c == QMetaObject::WriteProperty) {  
     89             void *_v = _a[0];  
     90             switch (_id) {  
     91             case 0: getPropertyA(*reinterpret_cast< QString*>(_v)); break;  
     92             case 1: getPropertyB(*reinterpret_cast< QString*>(_v)); break;  
     93             }  
     94             _id -= 2;  
     95         } else if (_c == QMetaObject::ResetProperty) {  
     96             switch (_id) {  
     97             case 0: resetPropertyA(); break;  
     98             case 1: resetPropertyB(); break;  
     99             }  
    100             _id -= 2;  
    101         } else if (_c == QMetaObject::QueryPropertyDesignable) {  
    102             _id -= 2;  
    103         } else if (_c == QMetaObject::QueryPropertyScriptable) {  
    104             _id -= 2;  
    105         } else if (_c == QMetaObject::QueryPropertyStored) {  
    106             _id -= 2;  
    107         } else if (_c == QMetaObject::QueryPropertyEditable) {  
    108             _id -= 2;  
    109         } else if (_c == QMetaObject::QueryPropertyUser) {  
    110             _id -= 2;  
    111         }  
    112     #endif // QT_NO_PROPERTIES  
    113         return _id;  
    114     }  
    115     // SIGNAL 0  
    116     void TestObject::clicked()  
    117     {  
    118         QMetaObject::activate(this, &staticMetaObject, 0, 0);  
    119     }  
    120     // SIGNAL 1  
    121     void TestObject::pressed()  
    122     {  
    123         QMetaObject::activate(this, &staticMetaObject, 1, 0);  
    124     }  
    125     QT_END_MOC_NAMESPACE  
    复制代码

    qt_meta_data_TestObject::定义的正是QMetaObject::d.data指向的信息块;

    qt_meta_stringdata_TestObject:定义的是QMetaObject::d.dataString指向的信息块;

    const QMetaObject TestObject::staticMetaObject :定义TestObject类的MetaObject实例,从中可以看出QMetaObject各个字段是如何被赋值的;

    const QMetaObject *TestObject::metaObject() const:重写了QObject::metaObject函数,返回上述的MetaObject实例指针。

    TestObject::qt_metacall()是重写QObject的方法,依据传入的参数来调用signal&slot或访问property,动态方法调用属性访问正是依赖于这个方法,在第四篇中会再讲到该方法。

    TestObject::clicked()和TestObject::pressed()正是对两个signal的实现,可见,signal其实就是一种方法,只不过这种方法由qt meta system来实现,不用我们自己实现。

     TestObject类的所有meta信息就存储在 qt_meta_data_TestObject和qt_meta_stringdata_TestObject这两个静态数据中。 QMetaObject的接口的实现正是基于这两块数据。下面就对这两个数据进行分块说明。

    复制代码
     1 static const uint qt_meta_data_TestObject[] = { 
     2 
     3 数据块一:
     4         // content:
     5        4,       // revision
     6        0,       // classname
     7 
     8        2,   14, // classinfo   
     9 
    10        4,   18, // methods
    11 
    12        2,   38, // properties
    13        1,   44, // enums/sets
    14        0,    0, // constructors
    15        0,       // flags
    16        2,       // signalCount
    17 
    18 这块数据可以被看做meta信息的头部,正好和QMetaObjectPrivate数据结构相对应,在QMetaObject的实现中,正是将这块数据映射为QMetaObjectPrivate进行使用的。
    19 
    20 第一行数据“4”:版本号;
    21 
    22 第二行数据“0”:类型名,该值是qt_meta_stringdata_TestObject的索引,qt_meta_stringdata_TestObject[0]这个字符串不正是类型名“TestObject”吗。
    23 
    24 第三行数据“2,14”,第一个表明有2个classinfo被定义,第二个是说具体的 classinfo信息在qt_meta_data_TestObject中的索引,qt_meta_data_TestObject[14]的位置两个 classinfo名值对的定义;
    25 
    26 第四行数据“4,18”,指明method的信息,模式同上;
    27 
    28 第五行数据“2,38”,指明property的信息,模式同上;
    29 第六行数据“1,14”,指明enum的信息,模式同上。 
    30 
    31 数据块二:
    32  // classinfo: key, value
    33       22,   11,
    34       44,   29,
    35 
    36 classinfo信息块。第一行“22,11”,22表明 qt_meta_stringdata_TestObject[22]处定义的字符串是classinfo的key,11表明 qt_meta_stringdata_TestObject[11]处的字符串就是value。第二行“44,29”定义第二个classinfo。
    37 
    38 数据块三:
    39  // signals: signature, parameters, type, tag, flags
    40       53,   52,   52,   52, 0x05,
    41       63,   52,   52,   52, 0x05,
    42 
    43 signal信息块。第一行“53,   52,   52,   52, 0x05”定义第一个signal clicked()。qt_meta_stringdata_TestObject[53]是signal名称字符串。parameters 52, type 52, tag 52, flags如何解释暂未知。
    44 
    45 数据块四:
    46  // slots: signature, parameters, type, tag, flags
    47       73,   52,   52,   52, 0x0a,
    48       91,   52,   52,   52, 0x0a,
    49 
    50 slots信息,模式类似signal。
    51 
    52 数据块五:
    53  // properties: name, type, flags
    54      113,  105, 0x0a095007,
    55      123,  105, 0x0a095007,
    56 
    57 property性信息,模式类signal和slots,105如何和type对应暂未知。
    58 
    59 数据块六:
    60  // enums: name, flags, count, data
    61      133, 0x0,    2,   48,
    62  // enum data: key, value
    63      142, uint(TestObject::EnumValueA),
    64      153, uint(TestObject::EnumValueB),
    65 
    66 enum信息,第一行定义的是枚举名,flag,值的数目,data48不知是什么。
    67 
    68 几行定义的是各枚举项的名称和值。名称同上都是qt_meta_stringdata_TestObject的索引值。
    69 
    70        0        // eod
    71 };
    72 
    73 static const char qt_meta_stringdata_TestObject[] = {
    74 
    75 这块数据就是meta信息所需的字符串。是一个字符串的序列。
    76     "TestObjectLong HuihuAuthor"
    77     "TestObjectV1.0Versionclicked()"
    78     "pressed()onEventA(QString)onEventB(int)"
    79     "QStringpropertyApropertyBTestEnum"
    80     "EnumValueAEnumValueB"
    81 };
    复制代码

    本篇从Qt MetaObject源代码解读相关接口的实现,这些接口都定义于qmetaobject.cpp中。

    QMetaObject::className()

    1 inline const char *QMetaObject::className() const
    2 { return d.stringdata; }

    d.stringdata就是那块字符串数据,包含若干c字符串(以'')结尾。如果把d.stringdata当做一个c字符串指针的话,就是这个字符串序列的第一个字符串,正是类名。

    QMetaObject::superClass()

    1 inline const QMetaObject *QMetaObject::superClass() const
    2 { return d.superdata; }

    QMetaObject::classInfoCount()

    复制代码
     1 int QMetaObject::classInfoCount() const
     2 {
     3     int n = priv(d.data)->classInfoCount;
     4     const QMetaObject *m = d.superdata;
     5     while (m) {
     6         n += priv(m->d.data)->classInfoCount;
     7         m = m->d.superdata;
     8     }
     9     return n;
    10 }
    复制代码

    从代码可以看出,返回该类的所有classinfo数目,包括所有基类的。

    函数priv是一个简单inline函数:

    1 static inline const QMetaObjectPrivate *priv(const uint* data)
    2 { return reinterpret_cast<const QMetaObjectPrivate*>(data); }

    d.data指向的是那块二进制信息,priv将d.data解释为QMetaObjectPrivate。

    QMetaObjectPrivate是QMetaObject的私有实现类,其数据定义部分如下(见头文件qmetaobject_p.h)。和前一篇的示例moc文件内容一对应,其含义一目了然。

    复制代码
     1 struct QMetaObjectPrivate
     2 {
     3     int revision;
     4     int className;
     5     int classInfoCount, classInfoData;
     6     int methodCount, methodData;
     7     int propertyCount, propertyData;
     8     int enumeratorCount, enumeratorData;
     9     int constructorCount, constructorData; //since revision 2
    10     int flags; //since revision 3
    11     int signalCount; //since revision
    12 }
    复制代码

    QMetaObject:: classInfoOffset ()

    复制代码
     1 int QMetaObject::classInfoOffset() const
     2 {
     3     int offset = 0;
     4     const QMetaObject *m = d.superdata;
     5     while (m) {
     6         offset += priv(m->d.data)->classInfoCount;
     7         m = m->d.superdata;
     8     }
     9     return offset;
    10 }
    复制代码

    该类的含义是返回这个类所定义的classinfo的起始索引值,相当于它的祖先类所定义的classinfo的数量

    QMetaObject:: classInfo  (int index)

    复制代码
     1 QMetaClassInfo QMetaObject::classInfo(int index) const
     2 {
     3     int i = index;
     4     i -= classInfoOffset();
     5     if (i < 0 && d.superdata)
     6         return d.superdata->classInfo(index);
     7 
     8     QMetaClassInfo result;
     9     if (i >= 0 && i < priv(d.data)->classInfoCount) {
    10         result.mobj = this;
    11         result.handle = priv(d.data)->classInfoData + 2*i;
    12     }
    13     return result;
    14 }
    复制代码
    复制代码
     1 class Q_CORE_EXPORT QMetaClassInfo
     2 {
     3 public:
     4     inline QMetaClassInfo() : mobj(0),handle(0) {}
     5     const char *name() const;
     6     const char *value() const;
     7     inline const QMetaObject *enclosingMetaObject() const { return mobj; }
     8 private:
     9     const QMetaObject *mobj;
    10     uint handle;
    11     friend struct QMetaObject;
    12 };
    复制代码

    这个代码的流程比较简单。priv(d.data)->classInfoData是classinfo的信息在d.data中的偏移;每条classinfo信息占2个UINT的大小,因此“priv(d.data)->classInfoData + 
    2*i”这个表达式的值就是第i个classinfo的信息在d.data中的偏移。

    QMetaObject:: indexOfClassInfo  ()

    复制代码
     1 int QMetaObject::indexOfClassInfo(const char *name) const
     2 {
     3     int i = -1;
     4     const QMetaObject *m = this;
     5     while (m && i < 0) {
     6         for (i = priv(m->d.data)->classInfoCount-1; i >= 0; --i)
     7             if (strcmp(name, m->d.stringdata
     8                        + m->d.data[priv(m->d.data)->classInfoData + 2*i]) == 0) {
     9                 i += m->classInfoOffset();
    10                 break;
    11             }
    12         m = m->d.superdata;
    13     }
    14     return i;
    15 }
    复制代码

    按照继承层次,从下往上寻找名字为name的classinfo。

    参考前一函数的解释,表达式m->d.data[priv(m->d.data)->classInfoData + 2*i]的值是第i个classinfo信息的第一个32位值。该值是字符信息块d.stringdata中的索引值。因此 m->d.stringdata+ m->d.data[priv(m->d.data)->classInfoData + 2*i]就是classinfo名称的字符串。

    int constructorCount () const

    1 int QMetaObject::constructorCount() const
    2 {
    3     if (priv(d.data)->revision < 2)
    4         return 0;
    5     return priv(d.data)->constructorCount;
    6 }

    QMetaMethod constructor  ( int index ) const

    复制代码
     1 QMetaMethod QMetaObject::constructor(int index) const
     2 {
     3     int i = index;
     4     QMetaMethod result;
     5     if (priv(d.data)->revision >= 2 && i >= 0 && i < priv(d.data)->constructorCount) {
     6         result.mobj = this;
     7         result.handle = priv(d.data)->constructorData + 5*i;
     8     }
     9     return result;
    10 }
    复制代码

    int indexOfConstructor  ( const char * constructor ) const

    复制代码
     1 int QMetaObject::indexOfConstructor(const char *constructor) const
     2 {
     3     if (priv(d.data)->revision < 2)
     4         return -1;
     5     for (int i = priv(d.data)->constructorCount-1; i >= 0; --i) {
     6         const char *data = d.stringdata + d.data[priv(d.data)->constructorData + 5*i];
     7         if (data[0] == constructor[0] && strcmp(constructor + 1, data + 1) == 0) {
     8             return i;
     9         }
    10     }
    11     return -1;
    12 }
    复制代码
    复制代码
    1 int enumeratorCount () const
    2 
    3 int enumeratorOffset () const
    4 
    5 QMetaEnum enumerator ( int index ) const
    6 
    7 int indexOfEnumerator ( const char * name ) const
    复制代码

    这组函数与classinfo那一组的实现及其相似。

    1     int methodCount () const 略;
    2     int methodOffset () const 略;
    复制代码
     1 QMetaMethod QMetaObject::method(int index) const
     2 {
     3     int i = index;
     4     i -= methodOffset();
     5     if (i < 0 && d.superdata)
     6         return d.superdata->method(index);
     7 
     8     QMetaMethod result;
     9     if (i >= 0 && i < priv(d.data)->methodCount) {
    10         result.mobj = this;
    11         result.handle = priv(d.data)->methodData + 5*i;
    12     }
    13     return result;
    14 }
    复制代码

    该函数的实现方式也一目了然。

    1 int indexOfMethod ( const char * method ) const 略;
    复制代码
     1 int QMetaObject::indexOfSignal(const char *signal) const
     2 {
     3     const QMetaObject *m = this;
     4     int i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, false);
     5     if (i < 0) {
     6         m = this;
     7         i = QMetaObjectPrivate::indexOfSignalRelative(&m, signal, true);
     8     }
     9     if (i >= 0)
    10         i += m->methodOffset();
    11     return i;
    12 }
    复制代码
    复制代码
     1 int QMetaObjectPrivate::indexOfSignalRelative(const QMetaObject **baseObject,
     2                                               const char *signal,
     3                                               bool normalizeStringData)
     4 {
     5     int i = indexOfMethodRelative<MethodSignal>(baseObject, signal, normalizeStringData);
     6 #ifndef QT_NO_DEBUG
     7     const QMetaObject *m = *baseObject;
     8     if (i >= 0 && m && m->d.superdata) {
     9         int conflict = m->d.superdata->indexOfMethod(signal);
    10         if (conflict >= 0)
    11             qWarning("QMetaObject::indexOfSignal: signal %s from %s redefined in %s",
    12                      signal, m->d.superdata->d.stringdata, m->d.stringdata);
    13     }
    14 #endif
    15     return i;
    16 }
    复制代码
    复制代码
     1 template<int MethodType>
     2 static inline int indexOfMethodRelative(const QMetaObject **baseObject,
     3                                         const char *method,
     4                                         bool normalizeStringData)
     5 {
     6     for (const QMetaObject *m = *baseObject; m; m = m->d.superdata) {
     7         int i = (MethodType == MethodSignal && priv(m->d.data)->revision >= 4)
     8                 ? (priv(m->d.data)->signalCount - 1) : (priv(m->d.data)->methodCount - 1);
     9         const int end = (MethodType == MethodSlot && priv(m->d.data)->revision >= 4)
    10                         ? (priv(m->d.data)->signalCount) : 0;
    11         if (!normalizeStringData) {
    12             for (; i >= end; --i) {
    13                 const char *stringdata = m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5*i];
    14                 if (method[0] == stringdata[0] && strcmp(method + 1, stringdata + 1) == 0) {
    15                     *baseObject = m;
    16                     return i;
    17                 }
    18             }
    19         } else if (priv(m->d.data)->revision < 5) {
    20             for (; i >= end; --i) {
    21                 const char *stringdata = (m->d.stringdata + m->d.data[priv(m->d.data)->methodData + 5 * i]);
    22                 const QByteArray normalizedSignature = QMetaObject::normalizedSignature(stringdata);
    23                 if (normalizedSignature == method) {
    24                     *baseObject = m;
    25                     return i;
    26                 }
    27             }
    28         }
    29     }
    30     return -1;
    31 }
    复制代码

    可以看出,查找signal的特别之处在于,通过method元数据的第五项来判断这是不是一个signal。

    1 int indexOfSlot ( const char * slot ) const 略;
    2 int propertyCount () const 略;
    3 int propertyOffset () const 略;
    4 int indexOfProperty ( const char * name ) const 略;
    复制代码
     1 QMetaProperty QMetaObject::property(int index) const
     2 {
     3     int i = index;
     4     i -= propertyOffset();
     5     if (i < 0 && d.superdata)
     6         return d.superdata->property(index);
     7 
     8     QMetaProperty result;
     9     if (i >= 0 && i < priv(d.data)->propertyCount) {
    10         int handle = priv(d.data)->propertyData + 3*i;
    11         int flags = d.data[handle + 2];
    12         const char *type = d.stringdata + d.data[handle + 1];
    13         result.mobj = this;
    14         result.handle = handle;
    15         result.idx = i;
    16 
    17         if (flags & EnumOrFlag) {
    18             result.menum = enumerator(indexOfEnumerator(type));
    19             if (!result.menum.isValid()) {
    20                 QByteArray enum_name = type;
    21                 QByteArray scope_name = d.stringdata;
    22                 int s = enum_name.lastIndexOf("::");
    23                 if (s > 0) {
    24                     scope_name = enum_name.left(s);
    25                     enum_name = enum_name.mid(s + 2);
    26                 }
    27                 const QMetaObject *scope = 0;
    28                 if (scope_name == "Qt")
    29                     scope = &QObject::staticQtMetaObject;
    30                 else
    31                     scope = QMetaObject_findMetaObject(this, scope_name);
    32                 if (scope)
    33                     result.menum = scope->enumerator(scope->indexOfEnumerator(enum_name));
    34             }
    35         }
    36     }
    37     return result;
    38 }
    复制代码

    该函数的特别之处在于,如果这个propery是一个枚举类型的话,就为返回值QMetaPropery赋上正确QMetaEnum属性值。

    复制代码
     1 QMetaProperty QMetaObject::userProperty() const
     2 {
     3     const int propCount = propertyCount();
     4     for (int i = propCount - 1; i >= 0; --i) {
     5         const QMetaProperty prop = property(i);
     6         if (prop.isUser())
     7             return prop;
     8     }
     9     return QMetaProperty();
    10 }
    复制代码

    从这个函数的实现来看,一个QObject应该只会有一个打开USER flag的property。

    看一个整体例子

    源码:

    结果:

    复制代码
     1 QT_BEGIN_MOC_NAMESPACE
     2 static const uint qt_meta_data_MainWindow[] = {
     3 
     4  // content:
     5        6,       // revision
     6        0,       // classname
     7        2,   14, // classinfo
     8        6,   18, // methods
     9        1,   48, // properties
    10        1,   51, // enums/sets
    11        0,    0, // constructors
    12        0,       // flags
    13        2,       // signalCount
    14 
    15  // classinfo: key, value
    16       20,   11,
    17       39,   27,
    18 
    19  // signals: signature, parameters, type, tag, flags
    20       48,   47,   47,   47, 0x05,
    21       69,   62,   58,   47, 0x05,
    22 
    23  // slots: signature, parameters, type, tag, flags
    24       86,   47,   47,   47, 0x09,
    25      100,   47,   47,   47, 0x08,
    26      115,   47,   47,   47, 0x0a,
    27      214,  138,  130,   47, 0x0a,
    28 
    29  // properties: name, type, flags
    30      290,  285, 0x01095003,
    31 
    32  // enums: name, flags, count, data
    33      302, 0x0,    2,   55,
    34 
    35  // enum data: key, value
    36      311, uint(MainWindow::EnumValueA),
    37      322, uint(MainWindow::EnumValueB),
    38 
    39        0        // eod
    40 };
    41 
    42 static const char qt_meta_stringdata_MainWindow[] = {
    43     "MainWindowlfsblackAuthorlfsblack-v1"
    44     "Versionclicked()intenable"
    45     "JustLookIt(bool)slotNewFile()"
    46     "slotOpenFile()slotSaveFile()QString"
    47     "mbool,mInt,m_char,mString,mm_bool,intptr,charptr,boolptr,mmm,mmmmm,mmm"
    48     "mmmmm"
    49     "resultLook(bool,int,char,QString,bool,int*,char*,bool*,bool,bool,bool)"
    50     "boolcolorDialogTestEnumEnumValueA"
    51     "EnumValueB"
    52 };
    复制代码

    所谓meta call就是通过object的meta system的支持来动态调用object的方法,metacall也是signal&slot的机制的基石。本篇通过参考源代码来探究meta call的实现方法

    复制代码
     1     static bool invokeMethod(QObject *obj, const char *member,
     2                              Qt::ConnectionType,
     3                              QGenericReturnArgument ret,
     4                              QGenericArgument val0 = QGenericArgument(0),
     5                              QGenericArgument val1 = QGenericArgument(),
     6                              QGenericArgument val2 = QGenericArgument(),
     7                              QGenericArgument val3 = QGenericArgument(),
     8                              QGenericArgument val4 = QGenericArgument(),
     9                              QGenericArgument val5 = QGenericArgument(),
    10                              QGenericArgument val6 = QGenericArgument(),
    11                              QGenericArgument val7 = QGenericArgument(),
    12                              QGenericArgument val8 = QGenericArgument(),
    13                              QGenericArgument val9 = QGenericArgument());
    14 
    15     static inline bool invokeMethod(QObject *obj, const char *member,
    16                              QGenericReturnArgument ret,
    17                              QGenericArgument val0 = QGenericArgument(0),
    18                              QGenericArgument val1 = QGenericArgument(),
    19                              QGenericArgument val2 = QGenericArgument(),
    20                              QGenericArgument val3 = QGenericArgument(),
    21                              QGenericArgument val4 = QGenericArgument(),
    22                              QGenericArgument val5 = QGenericArgument(),
    23                              QGenericArgument val6 = QGenericArgument(),
    24                              QGenericArgument val7 = QGenericArgument(),
    25                              QGenericArgument val8 = QGenericArgument(),
    26                              QGenericArgument val9 = QGenericArgument())
    27     {
    28         return invokeMethod(obj, member, Qt::AutoConnection, ret, val0, val1, val2, val3,
    29                 val4, val5, val6, val7, val8, val9);
    30     }
    31 
    32     static inline bool invokeMethod(QObject *obj, const char *member,
    33                              Qt::ConnectionType type,
    34                              QGenericArgument val0 = QGenericArgument(0),
    35                              QGenericArgument val1 = QGenericArgument(),
    36                              QGenericArgument val2 = QGenericArgument(),
    37                              QGenericArgument val3 = QGenericArgument(),
    38                              QGenericArgument val4 = QGenericArgument(),
    39                              QGenericArgument val5 = QGenericArgument(),
    40                              QGenericArgument val6 = QGenericArgument(),
    41                              QGenericArgument val7 = QGenericArgument(),
    42                              QGenericArgument val8 = QGenericArgument(),
    43                              QGenericArgument val9 = QGenericArgument())
    44     {
    45         return invokeMethod(obj, member, type, QGenericReturnArgument(), val0, val1, val2,
    46                                  val3, val4, val5, val6, val7, val8, val9);
    47     }
    48 
    49     static inline bool invokeMethod(QObject *obj, const char *member,
    50                              QGenericArgument val0 = QGenericArgument(0),
    51                              QGenericArgument val1 = QGenericArgument(),
    52                              QGenericArgument val2 = QGenericArgument(),
    53                              QGenericArgument val3 = QGenericArgument(),
    54                              QGenericArgument val4 = QGenericArgument(),
    55                              QGenericArgument val5 = QGenericArgument(),
    56                              QGenericArgument val6 = QGenericArgument(),
    57                              QGenericArgument val7 = QGenericArgument(),
    58                              QGenericArgument val8 = QGenericArgument(),
    59                              QGenericArgument val9 = QGenericArgument())
    60     {
    61         return invokeMethod(obj, member, Qt::AutoConnection, QGenericReturnArgument(), val0,
    62                 val1, val2, val3, val4, val5, val6, val7, val8, val9);
    63     }
    复制代码

    QMetaObject这个静态方法可以动态地调用obj对象名字为member的方法,type参数表明该调用时同步的还是异步的。ret是一个 通用的用来存储返回值的类型,后面的9个参数是用来传递调用参数的,QGenericArgument()是一种通用的存储参数值的类型。(这里让人感觉 比较奇怪的是Qt为什么不将这个参数列表弄成某种动态的形式,而是最多九个)

    所调用的方法必须是invocable的,也就是signal,slot或者是加了声明为Q_INVOCABLE的其他方法。

    复制代码
     1 bool QMetaObject::invokeMethod(QObject *obj,
     2                                const char *member,
     3                                Qt::ConnectionType type,
     4                                QGenericReturnArgument ret,
     5                                QGenericArgument val0,
     6                                QGenericArgument val1,
     7                                QGenericArgument val2,
     8                                QGenericArgument val3,
     9                                QGenericArgument val4,
    10                                QGenericArgument val5,
    11                                QGenericArgument val6,
    12                                QGenericArgument val7,
    13                                QGenericArgument val8,
    14                                QGenericArgument val9)
    15 {
    16     if (!obj)
    17         return false;
    18 
    19     QVarLengthArray<char, 512> sig;
    20     int len = qstrlen(member);
    21     if (len <= 0)
    22         return false;
    23     sig.append(member, len);
    24     sig.append('(');
    25 
    26     const char *typeNames[] = {ret.name(), val0.name(), val1.name(), val2.name(), val3.name(),
    27                                val4.name(), val5.name(), val6.name(), val7.name(), val8.name(),
    28                                val9.name()};
    29 
    30     int paramCount;
    31     for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) {
    32         len = qstrlen(typeNames[paramCount]);
    33         if (len <= 0)
    34             break;
    35         sig.append(typeNames[paramCount], len);
    36         sig.append(',');
    37     }
    38     if (paramCount == 1)
    39         sig.append(')'); // no parameters
    40     else
    41         sig[sig.size() - 1] = ')';
    42     sig.append('');
    43 
    44     int idx = obj->metaObject()->indexOfMethod(sig.constData());
    45     if (idx < 0) {
    46         QByteArray norm = QMetaObject::normalizedSignature(sig.constData());
    47         idx = obj->metaObject()->indexOfMethod(norm.constData());
    48     }
    49 
    50     if (idx < 0 || idx >= obj->metaObject()->methodCount()) {
    51         qWarning("QMetaObject::invokeMethod: No such method %s::%s",
    52                  obj->metaObject()->className(), sig.constData());
    53         return false;
    54     }
    55     QMetaMethod method = obj->metaObject()->method(idx);
    56     return method.invoke(obj, type, ret,
    57                          val0, val1, val2, val3, val4, val5, val6, val7, val8, val9);
    58 }
    复制代码

    先依据传递的方法名称和参数,构造完整的函数签名(存储在局部变量sig)。参数的类型名就是调用时传递时的参数静态类型,这里可不会有什么类型转换,这是运行时的行为,参数类型转换是编译时的行为。

    然后通过这个sig签名在obj中去查找该方法,查询的结果就是一个QMetaMethod值,再将调用委托给QMetaMethod::invoke方法。

    复制代码
      1 bool QMetaMethod::invoke(QObject *object,
      2                          Qt::ConnectionType connectionType,
      3                          QGenericReturnArgument returnValue,
      4                          QGenericArgument val0,
      5                          QGenericArgument val1,
      6                          QGenericArgument val2,
      7                          QGenericArgument val3,
      8                          QGenericArgument val4,
      9                          QGenericArgument val5,
     10                          QGenericArgument val6,
     11                          QGenericArgument val7,
     12                          QGenericArgument val8,
     13                          QGenericArgument val9) const
     14 {
     15     if (!object || !mobj)
     16         return false;
     17 
     18     Q_ASSERT(mobj->cast(object));
     19 
     20     // check return type
     21     if (returnValue.data()) {
     22         const char *retType = typeName();
     23         if (qstrcmp(returnValue.name(), retType) != 0) {
     24             // normalize the return value as well
     25             // the trick here is to make a function signature out of the return type
     26             // so that we can call normalizedSignature() and avoid duplicating code
     27             QByteArray unnormalized;
     28             int len = qstrlen(returnValue.name());
     29 
     30             unnormalized.reserve(len + 3);
     31             unnormalized = "_(";        // the function is called "_"
     32             unnormalized.append(returnValue.name());
     33             unnormalized.append(')');
     34 
     35             QByteArray normalized = QMetaObject::normalizedSignature(unnormalized.constData());
     36             normalized.truncate(normalized.length() - 1); // drop the ending ')'
     37 
     38             if (qstrcmp(normalized.constData() + 2, retType) != 0)
     39                 return false;
     40         }
     41     }
     42 
     43     // check argument count (we don't allow invoking a method if given too few arguments)
     44     const char *typeNames[] = {
     45         returnValue.name(),
     46         val0.name(),
     47         val1.name(),
     48         val2.name(),
     49         val3.name(),
     50         val4.name(),
     51         val5.name(),
     52         val6.name(),
     53         val7.name(),
     54         val8.name(),
     55         val9.name()
     56     };
     57     int paramCount;
     58     for (paramCount = 1; paramCount < MaximumParamCount; ++paramCount) {
     59         if (qstrlen(typeNames[paramCount]) <= 0)
     60             break;
     61     }
     62     int metaMethodArgumentCount = 0;
     63     {
     64         // based on QMetaObject::parameterNames()
     65         const char *names = mobj->d.stringdata + mobj->d.data[handle + 1];
     66         if (*names == 0) {
     67             // do we have one or zero arguments?
     68             const char *signature = mobj->d.stringdata + mobj->d.data[handle];
     69             while (*signature && *signature != '(')
     70                 ++signature;
     71             if (*++signature != ')')
     72                 ++metaMethodArgumentCount;
     73         } else {
     74             --names;
     75             do {
     76                 ++names;
     77                 while (*names && *names != ',')
     78                     ++names;
     79                 ++metaMethodArgumentCount;
     80             } while (*names);
     81         }
     82     }
     83     if (paramCount <= metaMethodArgumentCount)
     84         return false;
     85 
     86     // check connection type
     87     QThread *currentThread = QThread::currentThread();
     88     QThread *objectThread = object->thread();
     89     if (connectionType == Qt::AutoConnection) {
     90         connectionType = currentThread == objectThread
     91                          ? Qt::DirectConnection
     92                          : Qt::QueuedConnection;
     93     }
     94 
     95 #ifdef QT_NO_THREAD
     96     if (connectionType == Qt::BlockingQueuedConnection) {
     97         connectionType = Qt::DirectConnection;
     98     }
     99 #endif
    100 
    101     // invoke!
    102     void *param[] = {
    103         returnValue.data(),
    104         val0.data(),
    105         val1.data(),
    106         val2.data(),
    107         val3.data(),
    108         val4.data(),
    109         val5.data(),
    110         val6.data(),
    111         val7.data(),
    112         val8.data(),
    113         val9.data()
    114     };
    115     // recompute the methodIndex by reversing the arithmetic in QMetaObject::property()
    116     int idx_relative = ((handle - priv(mobj->d.data)->methodData) / 5);
    117     int idx_offset =  mobj->methodOffset();
    118     QObjectPrivate::StaticMetaCallFunction callFunction =
    119         (QMetaObjectPrivate::get(mobj)->revision >= 6 && mobj->d.extradata)
    120         ? reinterpret_cast<const QMetaObjectExtraData *>(mobj->d.extradata)->static_metacall : 0;
    121 
    122     if (connectionType == Qt::DirectConnection) {
    123         if (callFunction) {
    124             callFunction(object, QMetaObject::InvokeMetaMethod, idx_relative, param);
    125             return true;
    126         } else {
    127             return QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, idx_relative + idx_offset, param) < 0;
    128         }
    129     } else if (connectionType == Qt::QueuedConnection) {
    130         if (returnValue.data()) {
    131             qWarning("QMetaMethod::invoke: Unable to invoke methods with return values in "
    132                      "queued connections");
    133             return false;
    134         }
    135 
    136         int nargs = 1; // include return type
    137         void **args = (void **) qMalloc(paramCount * sizeof(void *));
    138         Q_CHECK_PTR(args);
    139         int *types = (int *) qMalloc(paramCount * sizeof(int));
    140         Q_CHECK_PTR(types);
    141         types[0] = 0; // return type
    142         args[0] = 0;
    143 
    144         for (int i = 1; i < paramCount; ++i) {
    145             types[i] = QMetaType::type(typeNames[i]);
    146             if (types[i]) {
    147                 args[i] = QMetaType::construct(types[i], param[i]);
    148                 ++nargs;
    149             } else if (param[i]) {
    150                 qWarning("QMetaMethod::invoke: Unable to handle unregistered datatype '%s'",
    151                          typeNames[i]);
    152                 for (int x = 1; x < i; ++x) {
    153                     if (types[x] && args[x])
    154                         QMetaType::destroy(types[x], args[x]);
    155                 }
    156                 qFree(types);
    157                 qFree(args);
    158                 return false;
    159             }
    160         }
    161 
    162         QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,
    163                                                         0, -1, nargs, types, args));
    164     } else { // blocking queued connection
    165 #ifndef QT_NO_THREAD
    166         if (currentThread == objectThread) {
    167             qWarning("QMetaMethod::invoke: Dead lock detected in "
    168                         "BlockingQueuedConnection: Receiver is %s(%p)",
    169                         mobj->className(), object);
    170         }
    171 
    172         QSemaphore semaphore;
    173         QCoreApplication::postEvent(object, new QMetaCallEvent(idx_offset, idx_relative, callFunction,
    174                                                         0, -1, 0, 0, param, &semaphore));
    175         semaphore.acquire();
    176 #endif // QT_NO_THREAD
    177     }
    178     return true;
    179 }
    复制代码

    代码首先检查返回值的类型是否正确;再检查参数的个数是否匹配,看懂这段代码需要参考该系列之二对moc文件的解析;再依据当前线程和被调对象所属 线程来调整connnection type;如果是directconnection,直接调用 QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, methodIndex, param),param是将所有参数值指针排列组成的指针数组。如果不是directconnection,也即异步调用,就通过一个post一个 QMetaCallEvent到obj,此时须将所有的参数复制一份存入event对象。

    QMetaObject::metacall的实现如下:

    复制代码
    1 int QMetaObject::metacall(QObject *object, Call cl, int idx, void **argv)
    2 {
    3     if (QMetaObject *mo = object->d_ptr->metaObject)
    4         return static_cast<QAbstractDynamicMetaObject*>(mo)->metaCall(cl, idx, argv);
    5     else
    6         return object->qt_metacall(cl, idx, argv);
    7 }
    复制代码

    如果object->d_ptr->metaObject(QMetaObjectPrivate)存在,通过该metaobject 来调用,这里要参考该系列之三对QMetaObjectPrivate的介绍,这个条件实际上就是object就是QObject类型,而不是派生类型。 否则调用object::qt_metacall。

    对于异步调用,QObject的event函数里有如下代码:

    复制代码
     1 class Q_CORE_EXPORT QMetaCallEvent : public QEvent
     2 {
     3 public:
     4     QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction , const QObject *sender, int signalId,
     5                    int nargs = 0, int *types = 0, void **args = 0, QSemaphore *semaphore = 0);
     6     ~QMetaCallEvent();
     7 
     8     inline int id() const { return method_offset_ + method_relative_; }
     9     inline const QObject *sender() const { return sender_; }
    10     inline int signalId() const { return signalId_; }
    11     inline void **args() const { return args_; }
    12 
    13     virtual void placeMetaCall(QObject *object);
    14 
    15 private:
    16     const QObject *sender_;
    17     int signalId_;
    18     int nargs_;
    19     int *types_;
    20     void **args_;
    21     QSemaphore *semaphore_;
    22     QObjectPrivate::StaticMetaCallFunction callFunction_;
    23     ushort method_offset_;
    24     ushort method_relative_;
    25 };
    复制代码
    复制代码
     1 class Q_CORE_EXPORT QObjectPrivate : public QObjectData
     2 {
     3     Q_DECLARE_PUBLIC(QObject)
     4 
     5 public:
     6     struct ExtraData
     7     {
     8         ExtraData() {}
     9 #ifndef QT_NO_USERDATA
    10         QVector<QObjectUserData *> userData;
    11 #endif
    12         QList<QByteArray> propertyNames;
    13         QList<QVariant> propertyValues;
    14     };
    15 
    16     typedef void (*StaticMetaCallFunction)(QObject *, QMetaObject::Call, int, void **);
    17     struct Connection
    18     {
    19         QObject *sender;
    20         QObject *receiver;
    21         StaticMetaCallFunction callFunction;
    22         // The next pointer for the singly-linked ConnectionList
    23         Connection *nextConnectionList;
    24         //senders linked list
    25         Connection *next;
    26         Connection **prev;
    27         QBasicAtomicPointer<int> argumentTypes;
    28         ushort method_offset;
    29         ushort method_relative;
    30         ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
    31         ~Connection();
    32         int method() const { return method_offset + method_relative; }
    33     };
    34     // ConnectionList is a singly-linked list
    35     struct ConnectionList {
    36         ConnectionList() : first(0), last(0) {}
    37         Connection *first;
    38         Connection *last;
    39     };
    40 
    41     struct Sender
    42     {
    43         QObject *sender;
    44         int signal;
    45         int ref;
    46     };
    复制代码
    复制代码
     1     case QEvent::MetaCall:
     2         {
     3 #ifdef QT_JAMBI_BUILD
     4             d_func()->inEventHandler = false;
     5 #endif
     6             QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e);
     7             QObjectPrivate::Sender currentSender;
     8             currentSender.sender = const_cast<QObject*>(mce->sender());
     9             currentSender.signal = mce->signalId();
    10             currentSender.ref = 1;
    11             QObjectPrivate::Sender * const previousSender =
    12                 QObjectPrivate::setCurrentSender(this, &currentSender);
    13 #if defined(QT_NO_EXCEPTIONS)
    14             mce->placeMetaCall(this);
    15 #else
    16             QT_TRY {
    17                 mce->placeMetaCall(this);
    18             } QT_CATCH(...) {
    19                 QObjectPrivate::resetCurrentSender(this, &currentSender, previousSender);
    20                 QT_RETHROW;
    21             }
    22 #endif
    23             QObjectPrivate::resetCurrentSender(this, &currentSender, previousSender);
    24             break;
    25         }
    复制代码

    QMetaCallEvent的代码很简单:

    复制代码
    1 void QMetaCallEvent::placeMetaCall(QObject *object)
    2 {
    3     if (callFunction_) {
    4         callFunction_(object, QMetaObject::InvokeMetaMethod, method_relative_, args_);
    5     } else {
    6         QMetaObject::metacall(object, QMetaObject::InvokeMetaMethod, method_offset_ + method_relative_, args_);
    7     }
    8 }
    复制代码

    殊途同归。

    最后来看一下object->qt_metacall是如何实现的,这又回到了上面所提供的示例moc文件中去了。该文件提供了该方法的实现:

    复制代码
     1 int TestObject::qt_metacall(QMetaObject::Call _c, int _id, void **_a)    
     2 # {    
     3 #     _id = QObject::qt_metacall(_c, _id, _a);    
     4 #     if (_id < 0)    
     5 #         return _id;    
     6 #     if (_c == QMetaObject::InvokeMetaMethod) {    
     7 #         switch (_id) {    
     8 #         case 0: clicked(); break;    
     9 #         case 1: pressed(); break;    
    10 #         case 2: onEventA((*reinterpret_cast< const QString(*)>(_a[1]))); break;    
    11 #         case 3: onEventB((*reinterpret_cast< int(*)>(_a[1]))); break;    
    12 #         default: ;    
    13 #         }    
    14 #         _id -= 4;    
    15 #     }    
    16 # #ifndef QT_NO_PROPERTIES    
    17 #       else if (_c == QMetaObject::ReadProperty) {    
    18 #         void *_v = _a[0];    
    19 #         switch (_id) {    
    20 #         case 0: *reinterpret_cast< QString*>(_v) = getPropertyA(); break;    
    21 #         case 1: *reinterpret_cast< QString*>(_v) = getPropertyB(); break;    
    22 #         }    
    23 #         _id -= 2;    
    24 #     } else if (_c == QMetaObject::WriteProperty) {    
    25 #         void *_v = _a[0];    
    26 #         switch (_id) {    
    27 #         case 0: getPropertyA(*reinterpret_cast< QString*>(_v)); break;    
    28 #         case 1: getPropertyB(*reinterpret_cast< QString*>(_v)); break;    
    29 #         }    
    30 #         _id -= 2;    
    31 #     } else if (_c == QMetaObject::ResetProperty) {    
    32 #         switch (_id) {    
    33 #         case 0: resetPropertyA(); break;    
    34 #         case 1: resetPropertyB(); break;    
    35 #         }    
    36 #         _id -= 2;    
    37 #     } else if (_c == QMetaObject::QueryPropertyDesignable) {    
    38 #         _id -= 2;    
    39 #     } else if (_c == QMetaObject::QueryPropertyScriptable) {    
    40 #         _id -= 2;    
    41 #     } else if (_c == QMetaObject::QueryPropertyStored) {    
    42 #         _id -= 2;    
    43 #     } else if (_c == QMetaObject::QueryPropertyEditable) {    
    44 #         _id -= 2;    
    45 #     } else if (_c == QMetaObject::QueryPropertyUser) {    
    46 #         _id -= 2;    
    47 #     }    
    48 # #endif // QT_NO_PROPERTIES    
    49 #     return _id;    
    50 # }  
    复制代码

    这段代码将调用最终转到我们自己的实现的函数中来。这个函数不经提供了metamethod的动态调用,而且也提供了property的动态操作方法。可想而知,property的动态调用的实现方式一定和invocalbe method是一致的。

    在qobjectdefs.h中有这样的定义:

    1 # define METHOD(a)   "0"#a
    2 # define SLOT(a)     "1"#a
    3 # define SIGNAL(a)   "2"#a

    不过是在方法签名之前加了一个数字标记。因为我们既可以将signal连接到slot,也可以将signal连接到signal,所有必须要有某种方法区分一下。

    QObject::connect()

    复制代码
      1 bool QObject::connect(const QObject *sender, const char *signal,
      2                       const QObject *receiver, const char *method,
      3                       Qt::ConnectionType type)
      4 {
      5     {
      6         const void *cbdata[] = { sender, signal, receiver, method, &type };
      7         if (QInternal::activateCallbacks(QInternal::ConnectCallback, (void **) cbdata))
      8             return true;
      9     }
     10 
     11 #ifndef QT_NO_DEBUG
     12     bool warnCompat = true;
     13 #endif
     14     if (type == Qt::AutoCompatConnection) {
     15         type = Qt::AutoConnection;
     16 #ifndef QT_NO_DEBUG
     17         warnCompat = false;
     18 #endif
     19     }
     20 
     21     if (sender == 0 || receiver == 0 || signal == 0 || method == 0) {
     22         qWarning("QObject::connect: Cannot connect %s::%s to %s::%s",
     23                  sender ? sender->metaObject()->className() : "(null)",
     24                  (signal && *signal) ? signal+1 : "(null)",
     25                  receiver ? receiver->metaObject()->className() : "(null)",
     26                  (method && *method) ? method+1 : "(null)");
     27         return false;
     28     }
     29     QByteArray tmp_signal_name;
     30 
     31     if (!check_signal_macro(sender, signal, "connect", "bind"))
     32         return false;
     33     const QMetaObject *smeta = sender->metaObject();
     34     const char *signal_arg = signal;
     35     ++signal; //skip code
     36     int signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);
     37     if (signal_index < 0) {
     38         // check for normalized signatures
     39         tmp_signal_name = QMetaObject::normalizedSignature(signal - 1);
     40         signal = tmp_signal_name.constData() + 1;
     41 
     42         smeta = sender->metaObject();
     43         signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, false);
     44     }
     45     if (signal_index < 0) {
     46         // re-use tmp_signal_name and signal from above
     47 
     48         smeta = sender->metaObject();
     49         signal_index = QMetaObjectPrivate::indexOfSignalRelative(&smeta, signal, true);
     50     }
     51     if (signal_index < 0) {
     52         err_method_notfound(sender, signal_arg, "connect");
     53         err_info_about_objects("connect", sender, receiver);
     54         return false;
     55     }
     56     signal_index = QMetaObjectPrivate::originalClone(smeta, signal_index);
     57     int signalOffset, methodOffset;
     58     computeOffsets(smeta, &signalOffset, &methodOffset);
     59     int signal_absolute_index = signal_index + methodOffset;
     60     signal_index += signalOffset;
     61 
     62     QByteArray tmp_method_name;
     63     int membcode = extract_code(method);
     64 
     65     if (!check_method_code(membcode, receiver, method, "connect"))
     66         return false;
     67     const char *method_arg = method;
     68     ++method; // skip code
     69 
     70     const QMetaObject *rmeta = receiver->metaObject();
     71     int method_index_relative = -1;
     72     switch (membcode) {
     73     case QSLOT_CODE:
     74         method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);
     75         break;
     76     case QSIGNAL_CODE:
     77         method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);
     78         break;
     79     }
     80 
     81     if (method_index_relative < 0) {
     82         // check for normalized methods
     83         tmp_method_name = QMetaObject::normalizedSignature(method);
     84         method = tmp_method_name.constData();
     85 
     86         // rmeta may have been modified above
     87         rmeta = receiver->metaObject();
     88         switch (membcode) {
     89         case QSLOT_CODE:
     90             method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, false);
     91             if (method_index_relative < 0)
     92                 method_index_relative = QMetaObjectPrivate::indexOfSlotRelative(&rmeta, method, true);
     93             break;
     94         case QSIGNAL_CODE:
     95             method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, false);
     96             if (method_index_relative < 0)
     97                 method_index_relative = QMetaObjectPrivate::indexOfSignalRelative(&rmeta, method, true);
     98             break;
     99         }
    100     }
    101 
    102     if (method_index_relative < 0) {
    103         err_method_notfound(receiver, method_arg, "connect");
    104         err_info_about_objects("connect", sender, receiver);
    105         return false;
    106     }
    107 
    108     if (!QMetaObject::checkConnectArgs(signal, method)) {
    109         qWarning("QObject::connect: Incompatible sender/receiver arguments"
    110                  "
            %s::%s --> %s::%s",
    111                  sender->metaObject()->className(), signal,
    112                  receiver->metaObject()->className(), method);
    113         return false;
    114     }
    115 
    116     int *types = 0;
    117     if ((type == Qt::QueuedConnection)
    118             && !(types = queuedConnectionTypes(smeta->method(signal_absolute_index).parameterTypes())))
    119         return false;
    120 
    121 #ifndef QT_NO_DEBUG
    122     if (warnCompat) {
    123         QMetaMethod smethod = smeta->method(signal_absolute_index);
    124         QMetaMethod rmethod = rmeta->method(method_index_relative + rmeta->methodOffset());
    125         check_and_warn_compat(smeta, smethod, rmeta, rmethod);
    126     }
    127 #endif
    128     if (!QMetaObjectPrivate::connect(sender, signal_index, receiver, method_index_relative, rmeta ,type, types))
    129         return false;
    130     const_cast<QObject*>(sender)->connectNotify(signal - 1);
    131     return true;
    132 }
    复制代码

    忽略细节,只关注主要的流程,这段代码的做的事情就是将signal在sender的meta system中的signal索引找出,以及接受者方法(signal或slot)在receiver的meta 
    system中的索引找出来。在委托QMetaObjectPrivate::connect()执行连接。

    复制代码
     1 bool QMetaObjectPrivate::connect(const QObject *sender, int signal_index,
     2                                  const QObject *receiver, int method_index,
     3                                  const QMetaObject *rmeta, int type, int *types)
     4 {
     5     QObject *s = const_cast<QObject *>(sender);
     6     QObject *r = const_cast<QObject *>(receiver);
     7 
     8     int method_offset = rmeta ? rmeta->methodOffset() : 0;
     9     QObjectPrivate::StaticMetaCallFunction callFunction =
    10         (rmeta && QMetaObjectPrivate::get(rmeta)->revision >= 6 && rmeta->d.extradata)
    11         ? reinterpret_cast<const QMetaObjectExtraData *>(rmeta->d.extradata)->static_metacall : 0;
    12 
    13     QOrderedMutexLocker locker(signalSlotLock(sender),
    14                                signalSlotLock(receiver));
    15 
    16     if (type & Qt::UniqueConnection) {
    17         QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
    18         if (connectionLists && connectionLists->count() > signal_index) {
    19             const QObjectPrivate::Connection *c2 =
    20                 (*connectionLists)[signal_index].first;
    21 
    22             int method_index_absolute = method_index + method_offset;
    23 
    24             while (c2) {
    25                 if (c2->receiver == receiver && c2->method() == method_index_absolute)
    26                     return false;
    27                 c2 = c2->nextConnectionList;
    28             }
    29         }
    30         type &= Qt::UniqueConnection - 1;
    31     }
    32 
    33     QObjectPrivate::Connection *c = new QObjectPrivate::Connection;
    34     c->sender = s;
    35     c->receiver = r;
    36     c->method_relative = method_index;
    37     c->method_offset = method_offset;
    38     c->connectionType = type;
    39     c->argumentTypes = types;
    40     c->nextConnectionList = 0;
    41     c->callFunction = callFunction;
    42 
    43     QT_TRY {
    44         QObjectPrivate::get(s)->addConnection(signal_index, c);
    45     } QT_CATCH(...) {
    46         delete c;
    47         QT_RETHROW;
    48     }
    49 
    50     c->prev = &(QObjectPrivate::get(r)->senders);
    51     c->next = *c->prev;
    52     *c->prev = c;
    53     if (c->next)
    54         c->next->prev = &c->next;
    55 
    56     QObjectPrivate *const sender_d = QObjectPrivate::get(s);
    57     if (signal_index < 0) {
    58         sender_d->connectedSignals[0] = sender_d->connectedSignals[1] = ~0;
    59     } else if (signal_index < (int)sizeof(sender_d->connectedSignals) * 8) {
    60         sender_d->connectedSignals[signal_index >> 5] |= (1 << (signal_index & 0x1f));
    61     }
    62 
    63     return true;
    64 }
    复制代码

    同样忽略细节,这段代码首先在connecttype要求UniqueConnection的时候检查一下是不是有重复的连接。然后创建一个
    QObjectPrivate::Connection结构,这个结构包含了sender,receiver,接受方法的method_index,然后
    加入到某个连接存储表中;连接存储表可能是一种hash结构,signal_index就是key。可以推测,当signal方法被调用时,一定会到这个
    结构中查找所有连接到该signal的connection,connection保存了receiver和method_index,由上一篇可知,可以很容易调用到receiver的对应method。

    转自:http://www.cnblogs.com/lfsblack/p/5286654.html

  • 相关阅读:
    Android 面试知识集1
    Android 开发自己的网络收音机2——电台列表(SlidingMenu侧滑栏)
    Android 程序drawable资源保存到data目录
    Android 开发自己的网络收音机1——功能要求及设计方案
    Android内存机制分析2——分析APP内存使用情况
    Android内存机制分析1——了解Android堆和栈
    Android Gallery实现3D相册(附效果图+Demo源码)
    Android 后台发送邮件 (收集应用异常信息+Demo代码)
    纯代码写UI的时候,如何指定style?
    解决SimpleCursorAdapter不能自动更新的问题
  • 原文地址:https://www.cnblogs.com/liushui-sky/p/6474105.html
Copyright © 2011-2022 走看看