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 <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 -> 父类元类对象 地址值

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容