OC 对象本质

  • 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 

       NSLog(@"NSObject class 实例对象成员变量(isa)所占用的大小>> %zu", class_getInstanceSize([obj class])); 

//引入头文件  #import 

        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   传入对象
 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 .类的关系(百度借图……)
OC 对象本质_第1张图片
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

OC 对象本质_第2张图片
ISA_Mask.png

superclass::

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

你可能感兴趣的:(OC 对象本质)