zoukankan      html  css  js  c++  java
  • OC对象的本质分析


    NSObject实例对象占用的内存大小分析

    将Objective-C转换为CC++代码

    下面的命令可以将Objective-C代码转换为CC++代码, 但是转换出来的代码仅供分析参考.

    xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o mainarm64.cpp

    如果需要链接其他框架,使用-framework参数。比如-framework UIKit.

    使用上面的命令可以窥探出NSObject的底层实现如下:

    1 //头文件定义
    2 @interface NSObject <NSObject> {
    3     Class isa;
    4 }
    5     
    6 //转换为C++代码后
    7 struct NSObject_IMPL {
    8     Class isa;  //64位系统中指针占8个字节
    9 };

    可见,Objective-C的面向对象是基于CC++的结构体实现的.

    NSObject实例对象的内存分配

    导入 objc/runtime.h 后可以使用函数class_getInstanceSize获得NSObject实例对象的成员变量所占用的大小为8个字节(内存对齐后的大小).

    class_getInstanceSize([NSObject class])

    导入 malloc/malloc.h后可以使用函数malloc_size获得NSObject实例对象指针指向内存的大小为16个字节.

    malloc_size((__bridge const void *)(obj))

    通过以下路径追踪NSObject对象的内存分配过程:

    1. allocWithZone:
    2. _objc_rootAllocWithZone()
    3. class_createInstance()
    4. _class_createInstanceFromZone
    5. instanceSize()

    其中instanceSize()函数返回要为NSObject实例对象分配的内存大小, 其实现如下:

    1 size_t instanceSize(size_t extraBytes) {
    2     size_t size = alignedInstanceSize() + extraBytes;
    3     // CF requires all objects be at least 16 bytes.
    4     if (size < 16) size = 16;
    5     return size;
    6 }

    可见NSObject实例对象占用的最小内存为16个字节.

    使用Xcode查看内存数据

    实时查看内存数据

    在Xcode菜单栏中通过以下顺序可以查看对象的内存数据

    Debug -> Debug Workfllow -> View Memory

    对一个NSObject实例对象来说,通过该方法观察到前8个字节为非0值,后8个字节为全为0,第17位开始为非0值。 由此可以推断,前8个字节代表isa指针,也从侧面验证了一个NSObject实例对象指针指向内存的大小为16个字节。

    常用的lldb指令

    print

    print用于打印指针地址,可缩写为p.

    po

    po用于打印对象,即print object.

    memory read

    • memory read用于读取内存,可以缩写为x。其使用方法为:
    x/数量|格式|字节数  内存地址  

    数量参数说明:
    要打印多少个数据,格式参数和字节参数说明怎么样打印这些数据。这些参数都可以省略,即直接使用memory read 内存地址.

    格式参数说明:

    x是16进制,f是浮点,d是10进制

    字节参数说明:

    b:byte 1字节,h:half word 2字节
    w:word 4字节,g:giant word 8字节
    

      

    例如,x/3xg表示以16进制的形式打印3个字节串,每个字节串包含8个字节。

    memory write

    memory write用于修改内存中的值. 使用方法为:

    memory  write  内存地址  新值

    继承关系的内存布局

    定义如下两个类:

     1 @interface Person : NSObject
     2 @property (nonatomic,assign) int age;
     3 @end
     4 
     5 @implementation Person
     6 @end
     7 
     8 @interface Student : Person
     9 @property (nonatomic,assign) int no;
    10 @end
    11 
    12 @implementation Student
    13 @end

    将其转换为C++代码可见其底层实现如下:

     1 struct NSObject_IMPL {
     2     Class isa;
     3 };
     4 
     5 struct Person_IMPL {
     6     struct NSObject_IMPL NSObject_IVARS;
     7     int _age;
     8 };
     9 
    10 struct Student_IMPL {
    11     struct Person_IMPL Person_IVARS;
    12     int _no;
    13 };

    可见子类实例对象的结构体中会包含父类的结构体,且父类的结构体存储在最前

    子类创建的实例其内存大小的分析

    定义如下一个类:

    1 @interface Person : NSObject
    2 @property (nonatomic,assign) int height;
    3 @property (nonatomic,assign) int age;
    4 @property (nonatomic,assign) int no;
    5 @end
    6 
    7 @implementation Person
    8 @end

    将其转换为C++代码:

     1 struct NSObject_IMPL {
     2     Class isa;
     3 };
     4 
     5 struct Person_IMPL {
     6     struct NSObject_IMPL NSObject_IVARS;  //8个字节
     7     int _height;    //4个字节
     8     int _age;    //4个字节
     9     int _no;    //4个字节
    10 };

    根据分析可知,Person_IMPL结构体内所有成员的总大小为20个字节,但由于内存对齐的原因,Person_IMPL结构体占用的内存大小应为24个字节。

    实际上,使用class_getInstanceSize打印的结果为24,但是使用malloc_size打印的结果为32. 可见,Person实例对象实际占用的内存大小为32字节,尽管其只使用了24个字节。

    alloc方法底层调用的方法为allocWithZone, 而allocWithZone方法则会调用calloc函数分配内存. 相关的源码可以在https://opensource.apple.com/tarballs/libmalloc/进行下载。

    根据源码可以发现,系统为了优化内存的使用效率,规定iOS中分配的内存大小都必须为16的倍数.


    OC对象的分类

    OC中的对象,主要可以分为三类:

    • instance对象(实例对象)

    • class对象 (类对象)

    • meta-class对象 (元类对象)

    instance对象(实例对象)

    instance对象(实例对象)就是通过类alloc出来的对象,每次类调用alloc都会生成一个新的instance对象。instance对象在内存中存储的主要信息有:

    • isa指针
    • 其他类的成员变量(值)
    1 NSObject *obj1 = [[NSObject alloc] init];
    2 NSObject *obj2 = [[NSObject alloc] init];

     

    源码如下:

    #include <objc/objc.h>

    1 /// An opaque type that represents an Objective-C class.
    2 typedef struct objc_class *Class;
    3 
    4 /// Represents an instance of a class.
    5 struct objc_object {
    6     Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
    7 };

    class对象 (类对象)

    obj1和obj2就是两个NSObject的实例对象,分别占据两块的不同的内存。

    每个类在内存中有且只有一个class对象(类对象),class对象在内存中存储的主要信息有:

    • isa指针
    • superclass指针
    • 类的属性信息(@property)
    • 类的对象方法信息(instance method)
    • 类的协议信息(protocol)
    • 类的成员变量信息(类型,名称)
    • ........

    源码如下:

    #include <objc/runtime.h>

     1 struct objc_class {
     2     Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
     3 
     4 #if !__OBJC2__
     5     Class _Nullable super_class                              OBJC2_UNAVAILABLE;
     6     const char * _Nonnull name                               OBJC2_UNAVAILABLE;
     7     long version                                             OBJC2_UNAVAILABLE;
     8     long info                                                OBJC2_UNAVAILABLE;
     9     long instance_size                                       OBJC2_UNAVAILABLE;
    10     struct objc_ivar_list * _Nullable ivars                  OBJC2_UNAVAILABLE;
    11     struct objc_method_list * _Nullable * _Nullable methodLists                    OBJC2_UNAVAILABLE;
    12     struct objc_cache * _Nonnull cache                       OBJC2_UNAVAILABLE;
    13     struct objc_protocol_list * _Nullable protocols          OBJC2_UNAVAILABLE;
    14 #endif
    15 
    16 } OBJC2_UNAVAILABLE;

    下面代码获取到的都是NSObject类对象:

    1 NSObject *obj1 = [[NSObject alloc] init];
    2 NSObject *obj2 = [[NSObject alloc] init];
    3 Class objClass1 = [obj1 class];
    4 Class objClass2 = [obj2 class];
    5 Class objClass3 = [NSObject class];
    6 Class objClass4 = object_getClass(obj1);
    7 Class objClass5 = object_getClass(obj2);
    8 Class objClass6 = objc_getClass("NSObject");

    meta-class对象 (元类对象)


    每个类在内存中有且只有一个meta-class对象(元类对象)。meta-class对象和class对象的内存结构是一样的,但是用途不一样。meta-class对象在内存中存储的主要信息有:

    • isa指针
    • superclass指针
    • 类方法信息(class method)
    • ……

    下面代码获取到的metaClass就是NSObject的meta-class对象:

    Class metaClass = object_getClass([NSObject class]);


    需要注意的是,以下代码获取到的是Class对象,并不是meta-class对象:可以通过class_isMetaClass方法判断一个Class对象是否为meta-class对象。

    1 Class objClass = [[NSObject class] class];

    注意点

    1. Class objc_getClass(const char *aClassName)

      • 传入字符串类名
      • 返回对应的类对象
    2. Class object_getClass(id obj)

      • 传入的obj可能是instance对象、class对象、meta-class对象
      • 如果传入的是instance对象,返回class对象
      • 如果传入的是class对象,返回meta-class对象
      • 如果传入的是meta-class对象,返回NSObject(基类)的meta-class对象
    3. -(Class)class、+ (Class)class 返回的就是类对象

  • 相关阅读:
    【bzoj4596】[Shoi2016]黑暗前的幻想乡 容斥原理+矩阵树定理
    【bzoj4832】[Lydsy1704月赛]抵制克苏恩 期望dp
    【bzoj3796】Mushroom追妹纸 hash/sa+kmp+二分
    【bzoj3309】DZY Loves Math 莫比乌斯反演+线性筛
    【bzoj2813】 奇妙的Fibonacci数列 线性筛
    面向对象实现简单的学生课程选择
    小案例--面向对象中实现分页
    初识面向对象四(装饰器/反射)
    python小技巧--控制台输出带颜色的文字方法
    初识面向对象三(经典类/多态/鸭子类型/初识封装)
  • 原文地址:https://www.cnblogs.com/junhuawang/p/13476881.html
Copyright © 2011-2022 走看看