iOS开发 — 类的结构分析

588 阅读5分钟

在日常的开发中,类和对象都是最常见的,前几篇文章研究了对象,从这篇开始我们来研究一下类。

类的结构

我们在objc-runtime-new.h文件中可以找到类的结构:

struct objc_class : objc_object {
    // Class ISA;       // 隐藏的isa,继承自objc_object,占8字节
    Class superclass;   // 父类,占8字节
    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();
    }
    
    /***此处省略若干行代码***/
}

从源码中我们可以看出类本质上是一个结构体,ISA是继承自objc_object,superclass是父类,cache是缓存,bits是存储的数据。 既然我们已经知道了类的结构,那么肯定也想知道属性和方法都存储在哪里。

属性和方法的存储

通过观察类的结构可以发现,bits应该是存储数据的地方,那么如何取到bits里的数据呢,这里就需要用到内存平移,先来看一下cache_t的结构:

struct cache_t {
    struct bucket_t *_buckets;
    mask_t _mask;
    mask_t _occupied;

public:
    struct bucket_t *buckets();
    mask_t mask();
    mask_t occupied();
    void incrementOccupied();
    void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
    void initializeToEmpty();

    mask_t capacity();
    bool isConstantEmptyCache();
    bool canBeFreed();

    static size_t bytesForCapacity(uint32_t cap);
    static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);

    void expand();
    void reallocate(mask_t oldCapacity, mask_t newCapacity);
    struct bucket_t * find(SEL sel, id receiver);

    static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn));
}

_buckets为结构体指针,占8字节,mask_t点进去是uint32_t类型,占4字节,由此我们可以算出cache总共占16字节。前面的ISA和superclass各占8字节,所以我们一共需要偏移32字节。

首先我们定义一个TestClass类,并且声明属性、成员变量和方法:
@interface TestClass : NSObject {
    NSString *title;
}

@property (nonatomic,copy) NSString *subtitle;
+ (void)ClassMethod;
- (void)InstanceMethod;
@end

在main.m中进行实例化:
TestClass *cls = [TestClass alloc];
Class pCls = object_getClass(cls);
NSLog(@"%@ - %p",cls,pCls);

通过lldb来查看pCls的内存:
(lldb) x/6xg pCls
0x100002580: 0x001d800100002559 0x0000000100aff140
0x100002590: 0x00000001003a2290 0x0000000000000000
0x1000025a0: 0x0000000100f43884 0x0000000100aff0f0
// 偏移32字节,故可以知道bits的地址是0x1000025a0
(lldb) p (class_data_bits_t *)0x1000025a0
(class_data_bits_t *) $1 = 0x00000001000025a0
(lldb) p $1->data()
(class_rw_t *) $2 = 0x0000000100f43880
// 查看data的数据结构
(lldb) p *$2
(class_rw_t) $3 = {
  flags = 2148139008
  version = 0
  ro = 0x0000000100002350
  methods = {
    list_array_tt<method_t, method_list_t> = {
       = {
        list = 0x0000000100002288
        arrayAndFlag = 4294976136
      }
    }
  }
  properties = {
    list_array_tt<property_t, property_list_t> = {
       = {
        list = 0x0000000100002338
        arrayAndFlag = 4294976312
      }
    }
  }
  protocols = {
    list_array_tt<unsigned long, protocol_list_t> = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  firstSubclass = nil
  nextSiblingClass = NSDate
  demangledName = 0x0000000000000000
}
// 查看properties里的数据
(lldb) p $3.properties
(property_array_t) $4 = {
  list_array_tt<property_t, property_list_t> = {
     = {
      list = 0x0000000100002338
      arrayAndFlag = 4294976312
    }
  }
}
(lldb) p $4.list
(property_list_t *) $5 = 0x0000000100002338
// 查看list的数据
(lldb) p *$5
(property_list_t) $6 = {
  entsize_list_tt<property_t, property_list_t, 0> = {
    entsizeAndFlags = 16
    count = 1
    first = (name = "subtitle", attributes = "T@\"NSString\",C,N,V_subtitle")
  }
}
// 产看methods的数据
(lldb) p $3.methods
(method_array_t) $7 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x0000000100002288
      arrayAndFlag = 4294976136
    }
  }
}
(lldb) p $7.list
(method_list_t *) $8 = 0x0000000100002288
(lldb) p *$8
(method_list_t) $9 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 4
    first = {
      name = "InstanceMethod"
      types = 0x0000000100001f8c "v16@0:8"
      imp = 0x0000000100001520 (LGTest`-[TestClass InstanceMethod] at TestClass.m:16)
    }
  }
}
// 对象方法
(lldb) p $9.get(0)
(method_t) $14 = {
  name = "InstanceMethod"
  types = 0x0000000100001f8c "v16@0:8"
  imp = 0x0000000100001520 (LGTest`-[TestClass InstanceMethod] at TestClass.m:16)
}
// C++方法
(lldb) p $9.get(1)
(method_t) $11 = {
  name = ".cxx_destruct"
  types = 0x0000000100001f8c "v16@0:8"
  imp = 0x00000001000015c0 (LGTest`-[TestClass .cxx_destruct] at TestClass.m:10)
}
// 属性subtitle的getter方法
(lldb) p $9.get(2)
(method_t) $12 = {
  name = "subtitle"
  types = 0x0000000100001f94 "@16@0:8"
  imp = 0x0000000100001550 (LGTest`-[TestClass subtitle] at TestClass.h:16)
}
// 属性subtitle的setter方法
(lldb) p $9.get(3)
(method_t) $13 = {
  name = "setSubtitle:"
  types = 0x0000000100001f9c "v24@0:8@16"
  imp = 0x0000000100001580 (LGTest`-[TestClass setSubtitle:] at TestClass.h:16)
}

由此我们可以知道属性存储于class_rw_t结构的properties中,对象方法和属性的getter/setter方法存储于class_rw_t结构的methods中,那么成员变量和类方法呢?我们继续探索:

// 探索ro
(lldb) p $3.ro
(const class_ro_t *) $15 = 0x0000000100002350
(lldb) p *$15
(const class_ro_t) $16 = {
  flags = 388
  instanceStart = 8
  instanceSize = 24
  reserved = 0
  ivarLayout = 0x0000000100001ee4 "\x02"
  name = 0x0000000100001eda "TestClass"
  baseMethodList = 0x0000000100002288
  baseProtocols = 0x0000000000000000
  ivars = 0x00000001000022f0
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000100002338
}
// 查看baseProperties
(lldb) p $16.baseProperties
(property_list_t *const) $17 = 0x0000000100002338
(lldb) p *$17
(property_list_t) $18 = {
  entsize_list_tt<property_t, property_list_t, 0> = {
    entsizeAndFlags = 16
    count = 1
    first = (name = "subtitle", attributes = "T@\"NSString\",C,N,V_subtitle")
  }
}
// 查看ivars
(lldb) p $16.ivars
(const ivar_list_t *const) $19 = 0x00000001000022f0
(lldb) p *$19
(const ivar_list_t) $20 = {
  entsize_list_tt<ivar_t, ivar_list_t, 0> = {
    entsizeAndFlags = 32
    count = 2
    first = {
      offset = 0x0000000100002538
      name = 0x0000000100001f2e "title"
      type = 0x0000000100001fa7 "@\"NSString\""
      alignment_raw = 3
      size = 8
    }
  }
}
// 成员变量
(lldb) p $20.get(0)
(ivar_t) $21 = {
  offset = 0x0000000100002538
  name = 0x0000000100001f2e "title"
  type = 0x0000000100001fa7 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
// 属性生成的带下划线的成员变量
(lldb) p $20.get(1)
(ivar_t) $22 = {
  offset = 0x0000000100002540
  name = 0x0000000100001f34 "_subtitle"
  type = 0x0000000100001fa7 "@\"NSString\""
  alignment_raw = 3
  size = 8
}
// 查看baseMethodList
(lldb) p $16.baseMethodList
(method_list_t *const) $23 = 0x0000000100002288
(lldb) p *$23
(method_list_t) $24 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 4
    first = {
      name = "InstanceMethod"
      types = 0x0000000100001f8c "v16@0:8"
      imp = 0x0000000100001520 (LGTest`-[TestClass InstanceMethod] at TestClass.m:16)
    }
  }
}
(lldb) p $24.get(0)
(method_t) $25 = {
  name = "InstanceMethod"
  types = 0x0000000100001f8c "v16@0:8"
  imp = 0x0000000100001520 (LGTest`-[TestClass InstanceMethod] at TestClass.m:16)
}
(lldb) p $24.get(1)
(method_t) $26 = {
  name = ".cxx_destruct"
  types = 0x0000000100001f8c "v16@0:8"
  imp = 0x00000001000015c0 (LGTest`-[TestClass .cxx_destruct] at TestClass.m:10)
}
(lldb) p $24.get(2)
(method_t) $27 = {
  name = "subtitle"
  types = 0x0000000100001f94 "@16@0:8"
  imp = 0x0000000100001550 (LGTest`-[TestClass subtitle] at TestClass.h:16)
}
(lldb) p $24.get(3)
(method_t) $28 = {
  name = "setSubtitle:"
  types = 0x0000000100001f9c "v24@0:8@16"
  imp = 0x0000000100001580 (LGTest`-[TestClass setSubtitle:] at TestClass.h:16)
}

通过探索class_rw_t结构中的class_ro_t类型的ro,我们可以得知成员变量存储于ro中的ivars里,属性和对象方法、getter/setter方法在ro中也有存储,那么还剩一个类方法没有找到,这时我们就需要向元类中探索:

(lldb) x/4xg pCls
0x100002580: 0x001d800100002559 0x0000000100aff140
0x100002590: 0x00000001003a2290 0x0000000000000000
// 位元算算出元类地址
(lldb) p/x 0x001d800100002559 & 0x00007ffffffffff8
(long) $1 = 0x0000000100002558
(lldb) x/6xg $1
0x100002558: 0x001d800100aff0f1 0x0000000100aff0f0
0x100002568: 0x0000000102951bd0 0x0000000100000003
0x100002578: 0x0000000102951b30 0x001d800100002559
(lldb) p (class_data_bits_t *)0x100002578
(class_data_bits_t *) $2 = 0x0000000100002578
(lldb) p *$2
(class_data_bits_t) $3 = (bits = 4338293552)
(lldb) p $2->data()
(class_rw_t *) $4 = 0x0000000102951b30
(lldb) p *$4
(class_rw_t) $5 = {
  flags = 2685075456
  version = 7
  ro = 0x0000000100002240
  methods = {
    list_array_tt<method_t, method_list_t> = {
       = {
        list = 0x0000000100002220
        arrayAndFlag = 4294976032
      }
    }
  }
  properties = {
    list_array_tt<property_t, property_list_t> = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  protocols = {
    list_array_tt<unsigned long, protocol_list_t> = {
       = {
        list = 0x0000000000000000
        arrayAndFlag = 0
      }
    }
  }
  firstSubclass = nil
  nextSiblingClass = 0x00007fff9f53bb28
  demangledName = 0x0000000000000000
}
(lldb) p $5.methods
(method_array_t) $6 = {
  list_array_tt<method_t, method_list_t> = {
     = {
      list = 0x0000000100002220
      arrayAndFlag = 4294976032
    }
  }
}
(lldb) p $6.list
(method_list_t *) $7 = 0x0000000100002220
(lldb) p *$7
(method_list_t) $8 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "ClassMethod"
      types = 0x0000000100001f8c "v16@0:8"
      imp = 0x00000001000014f0 (LGTest`+[TestClass ClassMethod] at TestClass.m:12)
    }
  }
}
(lldb) p $5.ro
(const class_ro_t *) $9 = 0x0000000100002240
(lldb) p *$9
(const class_ro_t) $10 = {
  flags = 389
  instanceStart = 40
  instanceSize = 40
  reserved = 0
  ivarLayout = 0x0000000000000000
  name = 0x0000000100001eda "TestClass"
  baseMethodList = 0x0000000100002220
  baseProtocols = 0x0000000000000000
  ivars = 0x0000000000000000
  weakIvarLayout = 0x0000000000000000
  baseProperties = 0x0000000000000000
}
(lldb) p $10.baseMethodList
(method_list_t *const) $11 = 0x0000000100002220
(lldb) p *$11
(method_list_t) $12 = {
  entsize_list_tt<method_t, method_list_t, 3> = {
    entsizeAndFlags = 26
    count = 1
    first = {
      name = "ClassMethod"
      types = 0x0000000100001f8c "v16@0:8"
      imp = 0x00000001000014f0 (LGTest`+[TestClass ClassMethod] at TestClass.m:12)
    }
  }
}

由此我们可以知道类方法是存储在元类中的。

总结

通过一系列的探索我们明白了类的结构和属性、方法的存储。

成员变量:保存在ivars中,objc_class->bits.data()->ro->ivars

属性:

• 保存在baseProperties中:objc_class->bits.data()->ro-> baseProperties

• 属性会自动添加成员变量保存在ivars中:objc_class->bits.data()->ro->ivars

• getter和setter方法保存在baseMethodList中:objc_class->bits.data()->ro-> baseMethodList

实例方法:保存在类的baseMethodList 中:objc_class->bits.data()->ro-> baseMethodList

类方法:保存在元类的baseMethodList 中:metaclass->bits.data()->ro-> baseMethodList