iOS底层-isa指针、class结构

isa指针

  1. isa指针
    1. instance的isa指向class
      1. 当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用
    2. class的isa指向meta-class
      1. 当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用

      图1

  2. superclass指针
    1. class对象的superclass指针
      1. 当Student的instance对象要调用Person的对象方法时,会先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用

        图1

    2. meta-class对象的superclass指针
      1. Student的class要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用

        图1

  3. isa、superclass总结
    • instance的isa指向class
    • class的isa指向meta-class
    • meta-class的isa指向基类的meta-class
    • 基类meta-class的isa指向它自己(基类meta-class)
    • class的superclass指向父类的class
      • 如果没有父类,superclass指针为nil
    • meta-class的superclass指向父类的meta-class
      • 基类的meta-class的superclass指向基类的class
    • instance调用对象方法的轨迹
      • isa找到class,方法不存在,就通过superclass找父类
    • class调用类方法的轨迹
      • isa找meta-class,方法不存在,就通过superclass找父类

    图1

    • 疑问:
      • 通过上面的图,如过一个类调用一个类方法,首先通过当前类的isa指针到当前类的元类中去找,找不到,会通过superclass指针,到其父类的元类中去找,如果父类此时是根类元类,而且根类元类中也没有该类方法,那么按照上图的分析
      • 根类元类会通过它的superclass指针找到它的(根类)类对象,那么问题来了? 类对象中只有对象方法呀? 难道会调用对象方法?
      • 结论:会到根类类对象中寻找同名的对象方法,有就调用,没有就报错
      • 也就是说,类对象不一定会调用类方法,也有可能调用对象方法
      • 举例证明:

          //NSObject的分类
          #import <Foundation/Foundation.h>
          @interface NSObject (Test)
          +(void)test;
          @end
                    
          #import "NSObject+Test.h"
          @implementation NSObject (Test)
          //+(void)test{
          //    NSLog(@"NSObject====+test  ===%p",self);
          //}
          -(void)test{
              NSLog(@"NSObject====-test  ===%p",self);
          }
          @end
                    
          //main文件
          #import <Foundation/Foundation.h>
          #import "NSObject+Test.h"
          @interface Person : NSObject
          +(void)test;
          @end
                    
          @implementation Person
                    
          //+(void)test{
          //    NSLog(@"Person====+test ===%p",self);
          //}
                    
          @end
                    
          int main(int argc, const char * argv[]) {
              @autoreleasepool {
                  NSLog(@"Person=====%p",[Person class]);
                  NSLog(@"NSObject======%p",[NSObject class]);
                  [Person test];
                  [NSObject test];
              }
              return 0;
           }
                     
           //打印
           2019-05-06 11:28:52.147363+0800 03-isa与superclass[3060:191786] Person=====0x1000011e0
          2019-05-06 11:28:52.147779+0800 03-isa与superclass[3060:191786] NSObject======0x7fff9840a140
          2019-05-06 11:28:52.147796+0800 03-isa与superclass[3060:191786] NSObject====-test  ===0x1000011e0
          2019-05-06 11:28:52.147808+0800 03-isa与superclass[3060:191786] NSObject====-test  ===0x7fff9840a140
        
        • 从打印可以看出,调用的是-test,调用了对象方法
  4. isa指针的值
    1. person对象的isa指向它的类对象,那么下面验证一下:

       @interface Person : NSObject
       @end
       @implementation Person
       @end
              
       int main(int argc, const char * argv[]) {
           @autoreleasepool {
               Person *person = [[Person alloc] init];
               Class personclass = [Person class];
               Class personMetaClass = object_getClass(personclass);
               /**
                通过打断点,输入lldb命令,获取到person对象的isa值
                p/x person->isa   输出:0x001d8001000014d1
                //获取到类对象的地址值
                p/x personclass   输出:0x00000001000014d0
                       
                会发现,person的isa指向的地址,并不等于类对象的地址。但是如果做如下操作,则会相等
                p/x 0x001d8001000014d1 & 0x0000000ffffffff8ULL  输出:0x00000001000014d0
                因此,person对象的isa指针不是直接等于类对象的地址,而是需要做一个 & ISA_MASK运算后,才等于类对象的地址。
                */
               NSLog(@"%p===%p===%p",person,personclass,personMetaClass);
           }
           return 0;
       }
      
      • 通过找源码objc4,获取到如下宏定义

           # if __arm64__
           #   define ISA_MASK        0x0000000ffffffff8ULL
           # elif __x86_64__
           #   define ISA_MASK        0x00007ffffffffff8ULL
           # endif
        

Class的结构

  1. Xcode中的Class结构
    1. Command右击Class,进入

       /// An opaque type that represents an Objective-C class.
       typedef struct objc_class *Class; //OC中的类 Class
              
       /// Represents an instance of a class.
       //一个类的实例对象
       struct objc_object {
           Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
       };
              
       /// A pointer to an instance of a class.
       //指向一个实例的指针
       typedef struct objc_object *id;
      
      1. Class 本质是一个结构体指针 :objc_class类型的结构体指针
      2. 实例对象本质是一个结构体: objc_object类型的结构体,包含一个成员变量isa
      3. id 就是objc_object类型的结构体指针
    2. Command右击objc_class,进入

       struct objc_class {
           Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
              
       #if !__OBJC2__
           Class _Nullable super_class                              OBJC2_UNAVAILABLE;
           const char * _Nonnull name                               OBJC2_UNAVAILABLE;
           long version                                             OBJC2_UNAVAILABLE;
           long info                                                OBJC2_UNAVAILABLE;
           long instance_size                                       OBJC2_UNAVAILABLE;
           struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
           struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
           struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
           struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
       #endif
              
       } OBJC2_UNAVAILABLE;
      
      1. 会发现 #if !__OBJC2__说明,OC-2.0已经过期了
      2. 而目前所有的Xcode都是OC-2.0因此,这个不能分析问题
  2. 源码中的Class结构
    1. 下载源码: https://opensource.apple.com/tarballs/objc4/
    2. XCode打开源码,搜索struct objc_class,定位到objc-runtime-new.h文件

       struct objc_class : objc_object {
           // Class ISA;
           Class superclass;
           cache_t cache;             // formerly cache pointer and vtable
           class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
              
           class_rw_t *data() { 
               return bits.data();
           }
           void setData(class_rw_t *newData) {
               bits.setData(newData);
           }
           ....成员函数....
       }
      
    3. 分析源码
      1. 源码是用C++写的,因此需要懂C++
        1. C++中,struct定义的就是一个类,不在是结构体,跟class定义的类基本一样
        2. 下面遇到struct类型就以类分析了,不按照结构体分析了
      2. 可以看出objc_class类继承自objc_object,那么进入objc_object查看

         union isa_t {
             isa_t() { }
             isa_t(uintptr_t value) : bits(value) { }
                    
             Class cls;
             uintptr_t bits;
         };
                    
         struct objc_object {
             private:
                 //从上面的isa_t的联合体看出,isa_t跟基本就是Class
                 //因此也可以写成: Class isa:
                 isa_t isa;
                        
             public:
                        
                 // ISA() assumes this is NOT a tagged pointer object
                 Class ISA();
                        
                 // getIsa() allows this to be a tagged pointer object
                 Class getIsa();
                            
                 ...成员函数...
         }
        
      3. 上面基本上可以写成如下(不考虑成员函数的情况下)

         struct objc_class {
             Class isa;
             Class superclass;
             cache_t cache;            
             class_data_bits_t bits;   
         }
        
      4. 那么问题来了,貌似少了很多信息,对象方法、类方法,存放哪里呢?
        1. 点击objc_class的成员函数data()的返回值class_rw_t进入查看

           struct class_rw_t {
               uint32_t flags;
               uint32_t version;
               const class_ro_t *ro;
               method_array_t methods;
               property_array_t properties;
               protocol_array_t protocols;
               Class firstSubclass;
               Class nextSiblingClass;
               char *demangledName;
           };
          
        2. 可以看到class_rw_t这个类中存放这方法列表、属性列表等信息,但是发现却少了成员列表,进入class_ro_t

           struct class_ro_t {
               uint32_t flags;
               uint32_t instanceStart;
               uint32_t instanceSize;
           #ifdef __LP64__
               uint32_t reserved;
           #endif
               const uint8_t * ivarLayout;
               const char * name;
               method_list_t * baseMethodList;
               protocol_list_t * baseProtocols;
               const ivar_list_t * ivars;
               const uint8_t * weakIvarLayout;
               property_list_t *baseProperties;
            };
          
          
      5. 从上面分析可知,基本上所有的东西都找到了,但是class_rw_t这个类,需要通过objc_class的成员函数data()获取,那么,data()内部实际是什么呢?

         class_rw_t *data() { 
             return bits.data();
         }
        
        1. 可以返现需要调用bits对象的data()成员函数就可以了,bits我们知道是class_data_bits_t类型,进入该类型,找到该成员函数如下:

            class_rw_t* data() {
               //注意,此时的bits,仅仅是class_data_bits_t类的一个成员变量
               // Values are the FAST_ flags above.
               //uintptr_t bits;
               return (class_rw_t *)(bits & FAST_DATA_MASK);
           }
          
        2. 因此,我们只要拿到objc_class类的bits成员变量,然后bits.bits & FAST_DATA_MASK,即可获取class_rw_t数据

      6. 精简图如下:

        图1

相关面试题

  1. 对象的isa指针指向哪里?
    1. instance对象的isa指向class对象
    2. class对象的isa指向meta-class对象
    3. meta-class对象的isa指向基类的meta-class对象
  2. OC的类信息存放在哪里?
    1. 对象方法、属性、成员变量、协议信息,存放在class对象中
    2. 类方法,存放在meta-class对象中
    3. 成员变量的具体值,存放在instance对象
Table of Contents