美文网首页
OC 对象本质

OC 对象本质

作者: user_bo | 来源:发表于2018-11-28 17:31 被阅读0次
    • 1 . 编写的Objective-C代码,其底层都是由c/c++ 代码实现的。

      OC语言 —> c/c++ 语言—>汇编语言—>机器语言

    • 2 . oc的对象是基于 c/c++ 的结构体实现的。

    • 3 . oc 转换成 c/c++ 的终端命令:

     clang -rewrite-objc <源文件> -o <目标文件>
    
     - 添加支持的平台
      xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc <源文件> -o <目标文件>
    //eg::
    //xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp
    // 模拟器 (i386) , 32bit (arm7) , 64bit(arm64)
    
    
    • 4 . NSObject 底层实现 转换成 c++
    @interface NSObject{
        Class isa;
    }
    @end
    
    //转化 ::IMPL ->implementation
    struct NSObject_IMPL {
        Class isa;
    };
    
    // Class -> typedef struct objc_class *Class; 类指针
    
    
    • 一个NSObject 对象占用多少内存?
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
       
            NSObject * obj = [NSObject new];
            
    // 引入头文件 #import <objc/runtime.h>
    
           NSLog(@"NSObject class 实例对象成员变量(isa)所占用的大小>> %zu", class_getInstanceSize([obj class])); 
    
    //引入头文件  #import <malloc/malloc.h>
    
            NSLog(@"obj 指针所指向内存的大小>> %zu",malloc_size((__bridge const void *)(obj)));
        }
        return 0;
    }
    
    /*结果
     NSObject class 实例对象的大小>> 8
     obj 指针所指向内存的大小>> 16
    */  
    
    /*
    (64bit 环境)
    一个类对象在创建时 , 系统分配了 16 个字节 ,内部成员变量  isa 占用了 8 个字节
    */ 
    
    
    • 5 . 增加成员变量 ,对象占用内存增加
    @interface Student : NSObjec
    {
        @public
        int _age;
        int _num;
    }
    @end
    
    @implementation Student
    @end
    
    // clang  64bit 得到 Student_IMPL
    struct NSObject_IMPL {
        Class isa;
    };
    
    struct Student_IMPL {
        struct NSObject_IMPL NSObject_IVARS;
        int _age;
        int _num;
    };
    
    // 探测内存占用 和结果
    /*
    Student class 实例对象的大小:: 16
    stu 指针所指向内存的大小:: 16
    */
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
    
            Student * stu = [Student new];
            
            NSLog(@"Student class 实例对象的大小:: %zu", class_getInstanceSize([stu class]));
    
            NSLog(@"stu 指针所指向内存的大小:: %zu",malloc_size((__bridge const void *)(stu)));
            
        }
        return 0;
    }
    /*
    新增成员变量,会占用内存
    */
    
    
    • 6 .类的继承 (结构体嵌套)
    @interface Student : NSObject
    {
        @public
        int _age;
        int _num;
    }
    @end
    
    @implementation Student
    @end
    
    @interface GraduateStudent : Student
    {
        @public
        int _score;
    }
    @end
    
    @implementation GraduateStudent
    @end
    
    // clang  结构体的嵌套结构
    struct NSObject_IMPL {
        Class isa;
    };
    
    struct Student_IMPL {
        struct NSObject_IMPL NSObject_IVARS;
        int _age;
        int _num;
    };
    
    struct GraduateStudent_IMPL {
        struct Student_IMPL Student_IVARS;
        int _score;
    };
    
    // 探测内存占用 和结果
    /*
     Student class 实例对象的大小:: 16
     stu 指针所指向内存的大小:: 16
     GraduateStudent class 实例对象的大小:: 24
     gStu 指针所指向内存的大小:: 32
    
    注释 :: iOS 内部 16位内存对齐(解决 24 32 的疑惑); 
    */
    
    int main(int argc, const char * argv[]) {
        @autoreleasepool {
    
            //class_getInstanceSize 返回内存对齐后的占用空间
    
            Student * stu = [Student new];
            
            NSLog(@"Student class 实例对象的大小:: %zu", class_getInstanceSize([stu class]));
    
            NSLog(@"stu 指针所指向内存的大小:: %zu",malloc_size((__bridge const void *)(stu)));
            
            GraduateStudent *gStu = [GraduateStudent new];
            
            NSLog(@"GraduateStudent class 实例对象的大小:: %zu", class_getInstanceSize([gStu class]));
            
            NSLog(@"gStu 指针所指向内存的大小:: %zu",malloc_size((__bridge const void *)(stu)));
    
        }
        return 0;
    }
    
    /*
    结构体内存对齐 三原则::
    * 1 内存自然对齐,结构体的总大小,必须是其内部最大成员的整数倍,不足的需要补齐;
    
    * 2 结构体或union联合的数据成员,第一个数据成员是要放在offset == 0的地方,
        如果遇上子成员,要根据子成员的类型存放在对应的整数倍的地址上;
    
    * 3 如果结构体作为成员,则要找到这个结构体中的最大元素,然后从这个最大成员的整数倍地址开始存储
      eg:(strutc a中有一个struct b,b里面有char,int,double….那b应该从double的整数倍开始存储);
    
    为何要内存对齐? (百度复制 666………… )
        1 平台原因(移植原因):不是所有的硬件平台都能访问任意地址上的任意数据的;
         某些硬件平台只能在某些地址处取某些特定类型的数据,否则抛出硬件异常。
        2 性能原因:经过内存对齐后,CPU的内存访问速度大大提升。
    */
    
    
    • 7 . 对象方法

      oc类的实例对象是以结构体实现的,同时这个结构体中包含了对象的实例变量,但是对象的方法并没有在结构体中体现
      (因为类对象可以创建多个,但是方法实现只需一份就足够了)

    • 8 .对象的分类

    a . 实例对象 (instance 对象)

    /*
     a . 实例对象  (instance 对象)
    通过类 alloc 出来的对象,每次alloc 都会产生新的对象,新的对象会开辟占据新的内存;
    
    实例对象在内存中存储的信息包括:
    isa 指针(继承NSObject)、
    成员变量(ivar 这里保存成员变量的值)
    */
    
     Student * student_0 = [[Student alloc] init];
     Student * student_1 = [[Student alloc] init];
    
    // student_0,student_1是Student实例对象;
    
    

    b . 类对象(class 对象 )

    /*
    b . 类对象(class 对象 )
    类产生的不同的实例对象指向同一个类对象;
    每个类在内存中有且只有一个类对象;
      
    类对象字在内存中存储的信息主要包括:
     isa 指针、
    superclass 指针 、
    类的属性信息(@property)、
    类的对象方法信息 (instance method(减号方法))、
    类的协议信息(protocal)、
    类的成员变量信息 (ivar 这里保存成员变量的类型,别名等)
    ……
    */
     Student * student_0 = [[Student alloc] init];
    
     Class student_0_class_0 = [student_0 class];
     Class student_0_class_1 = [Student class];
    // 引入头文件 #import <objc/runtime.h>  传入对象
     Class student_0_class_2 = object_getClass(student_0);
    
      NSLog(@" %p :: %p :: %p",student_0_class_0,student_0_class_1,student_0_class_2);     
    
    //打印结果(类唯一的类对象)  0x100001308 :: 0x100001308 :: 0x100001308
    
    

    c . 元类对象 (meta - class 对象)

    c . 元类对象 (meta - class 对象)
    /*
    每个类在内存中只有一个元类对象
    元类对象和类对象的内存结构是一样的,但是用途不一样;
    
    元类对象在内存中存储的信息主要包括::
    isa指针、
    superclass 指针、
    类方法信息(加号方法)
    ……
    */
    Student * student_0 = [[Student alloc] init];
    
    Class student_0_class_0 = [student_0 class];
    Class student_0_class_1 = [Student class];
    Class student_0_class_2 = object_getClass(student_0);
    
    // 元类对象 传入类对象
    Class student_0_mateClass = object_getClass([Student class]);
    
    NSLog(@" %p :: %p :: %p  %p",student_0_class_0,student_0_class_1,student_0_class_2,student_0_mateClass);
    
    //打印结果(类对象,元类对象) 0x100001308 :: 0x100001308 :: 0x100001308  0x1000012e0
    
    /*
    注释::[ [NSObject class] class] 返回的是类对象,并不是元类对象
    判断是否是元类对象
    Bool isMateClass = class_isMetaClass(student_0_mateClass) 
    */ 
    
    

    注释 :这两个方法是不同的

    Class objc_getClass (const char * aClassName)
    传入字符串 返回类对象
    
    Class object_getClass (id obj)
    传入实例对象 返回类对象
    传入类对象 返回元类对象
    传入元类对象 返回 NSObject(基类)的元类对象
    
    
    • 9 .类的关系(百度借图……)
    iOS 类的关系图.png

    指向关系

    /*
    1. 实例对象的isa 指向类对象;
    2. 类对象的isa 指向 元类对象
    3.元类对象的isa 指向 NSObject(基类)类对象
    
    4.类对象的superclass 指向父类的类对象(直到 NSObject(基类)类对象(NSObject 类对象的superclass nil))
    5.元类对象的superclass 指向 父类的元类对象(直到 NSObject(基类)元类对象)
    6基类的元类对象superclass 指向基类的类对象
    
    */
    
    

    通过解读指向关系,查阅 instance 调用对象方法的轨迹(objc_msgSend)

    obj[实例对象] 执行 instance_method 的过程::
    
    1. 通过 obj[实例对象] 的 isa 找到自己的 obj[类对象],
       查看方法列表中是否包含调用方法;
    (包含 -> 直接执行方法 ;不包含 ->  继续 2 )
    
    2.通过 obj[类对象] 的 superclass 找到 obj[父类_类对象];
       查看方法列表中是否包含调用方法;
    (包含 -> 直接执行方法 ;不包含 ->  继续 3 )
    
    3. 通过 obj[父类_类对象] 的 superclass 找到 下一个父类类对象;
       查看方法列表中是否包含调用方法;
    (包含 -> 直接执行方法 ;不包含 ->  继续 4 )
    
    4. 知道通过superclass 找到基类类对象,
       查看方法列表中是否包含调用方法;
    (包含 -> 直接执行方法 ;不包含 ->  报错 : unrecognize instance "instance_method" ) 
    
    

    类 ClassA 执行 class_method 的调用轨迹

    ClassA[类对象] 执行 class_method 的过程::
    
    1. 通过 ClassA[类对象]  的 isa 找到自己的 ClassA[元类对象],
       查看方法列表中是否包含调用方法;
    (包含 -> 直接执行方法 ;不包含 ->  继续 2 )
    
    2.通过 ClassA[元类对象] 的 superclass 找到 ClassA[父类_元类对象];
       查看方法列表中是否包含调用方法;
    (包含 -> 直接执行方法 ;不包含 ->  继续 3 )
    
    3. 通过 ClassA[父类_元类对象] 的 superclass 找到 下一个父类类对象;
       查看方法列表中是否包含调用方法;
    (包含 -> 直接执行方法 ;不包含 ->  继续 4 )
    
    4. 知道通过superclass 找到基类的元类对象,
       查看方法列表中是否包含调用方法;
    (包含 -> 直接执行方法 ;不包含 ->  继续 5  )
    
     *重要*  5. 基类的元类对象通过superclass 找到基类的类对象(查找基类类对象的对象方法,
       查看方法列表中是否包含调用方法;
    (包含 -> 直接执行方法 ;不包含 ->  报错 : unrecognize method "class_method"   )
    原因::方法执行的本质是 objc_msgSend(参数) 方法,本身并不会区分 类方法 对象方法。
      
    
    • 10 . isa 指针,superclass 指针

    isa::

    类对象的 isa -> 实例对象 isa 的值 & 指定环境的ISA_MASK
    元类对象的 isa -> 类对象 isa 的值 & 指定环境的ISA_MASK

    ISA_Mask.png

    superclass::

    子类类对象的superclass -> 父类类对象 地址值
    子类元类对象的superclass -> 父类元类对象 地址值

    相关文章

      网友评论

          本文标题:OC 对象本质

          本文链接:https://www.haomeiwen.com/subject/tavaqqtx.html