浅谈对Objective-C的对象本质的理解

1. Objective-C的本质

我们平时编写的OC代码,其实底层实现都是C/C++代码,类主要是基于C/C++的结构体的数据结构实现的,因为对象或者类有各种类型(NSArray *,NSDictionary *,CFfloat等),因为可以存储不同种类的数据,能够满足的这样的结构就是结构体.
为了证明OC的结构,所以可以转换成C++的代码,窥探内部的结构(有时候C++的代码也不一定能完全表示源码的情况,需要调试到汇编代码或源码查看).
我们可以通过终端进入到要窥探所在文件的位置,使用命令xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc main.m -o main.cpp.(如果电脑上面安装了多个版本的Xcode,转换为C++代码的时候会提示各种框架找不到的错误,一般是因为多个版本的Xcode路径冲突导致的,我们需要在终端指定一个Xcode的路径,例:sudo xcode-select --switch/Applications/Xcode10.0.app/Contents/Developer/).

注释:解释各种参数的翻译
xc就是Xcode的缩写。
xcrun是Xcode的一种工具。
-sdk iphoneos规定sdk需要运行在iOS系统上面。
clang是Xcode内置的llvm编译器前端,也是编译器的一种。
-arch xxx(arm64、i386、armv7...)指出iOS设备的架构。
参数 -rewrite-objc xx.m 是重写objc代码的指令(即重写xx.m文件) 。
-o newFileName.cpp 表示输出新的.cpp文件。

2. NSObject底层实现原理

NSObject底层原理.png

Class 定义为 :typedef struct objc_class *Class;也就是说Class是个结构体指针.
代码中[NSObject alloc]开辟空间给NSObject。obj的指针指向了isa的地址.isa的地址就是结构体的地址,原因是结构体的地址就是结构体中第一个成员的地址,而结构体只有一个成员,即isa指针的地址.

一. 例:student底层的原理

Student普通的结构.png

答:因为Student继承NSObject,也就继承了NSObject的数据结构,所以继承NSObject的8个字节,也就是NSobject中的isa的大小。

思考题:Student继承Person的结构.png

Person占据class_getInstanceSize=16 malloc_size=16, Student占据class_getInstanceSize=16 malloc_size=16,Person的变量实际用了12,但是由于内存对齐所以占用16.

二. 两种方法看内存大小

我们有这种方法在OC中表达一个类内存的大小.

<objc/runtime.h>文件提供class_getInstanceSize(Class _Nullable cls)方法,返回我们一个OC对象
的实例所占用的内存大小(可以说是结构体内存对齐之后的大小,8的倍数);
<malloc/malloc.h>文件提供 size_t malloc_size(const void *ptr)方法返回系统为这个对象分配的
内存大小(16的倍数)。

三. 内存对齐的原理(不全,后期添加)

我们先来看一些内存的例子,更加方便我们去理解内存分配和内存对齐原理:

  • 看一个没有成员变量的类的实例(以NSObject为例)
    NSObject *obj = [[NSObject alloc] init];
    NSLog(@"NSObject实例大小--> %zd",class_getInstanceSize([obj class]));
    NSLog(@"obj实际分配的内存%zd",malloc_size((const void *)obj));
//    NSObject实例大小--> 8
//    obj实际分配的内存16
  • 一个普通的类的实例,并且实例有且仅有唯一的成员变量(如:Student只有一个name属性)
@interface Student: NSObject
@property (nonatomic, copy) NSString *name;
@end;

@implementation  Student
@end;

Student *stu = [[Student alloc] init];
stu.name = @"Object-C";
NSLog(@"Student实例大小--> %zd",class_getInstanceSize([stu class]));
NSLog(@"stu实际分配的内存%zd",malloc_size((const void *)stu));
//     Student实例大小--> 16
//     stu实际分配的内存16
  • 一个普通的类的实例,并且实例有自己的成员变量(如:Student类,为其添加属性age、name等)
@interface Student: NSObject
@property (nonatomic, copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
@end;

@implementation  Student
@end;

Student *stu = [Student new];
stu.name = @"Object-C";
stu.age = 25;
NSLog(@"Student实例大小--> %zd",class_getInstanceSize([stu class]));
NSLog(@"stu实际分配的内存%zd",malloc_size((const void *)stu));
//     Student实例大小-->24
//     stu实际分配的内存32

由以上三次测试:一个OC对象所占用的内存取决于这个对象成员变量的多少.但是同时,系统为其分配内存时,默认会分配最少16个字节的大小.OC中对象的内存小于16就等于16(是Core Foundation的规定),下面是Core Foundation的源码.

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

内存对齐的原则:结构体的大小必须是最大成员的倍数.
更多的内存对齐的知识--内存对齐
补充:sizeof不是个函数是个运算符,传入的时候是类型不是具体的对象,sizeof是在编译的时候进行计算的.

3. OC对象的分类

objective-C的对象,简称为OC对象,分为三种:

  1. instance对象(实例对象).
  2. class对象(类对象).
  3. meta-class(元类对象).

一. 实例对象

NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];

object1、object2是NSObject的instance对象(实例对象),它们是不同的两个对象,分别占据着两块不同的内存。instance对象是通过类alloc出来的对象,每次调用alloc都会产生新的instance对象.instance对象在内存中存储的信息包括:isa指针,其他成员变量。
实例对象存放的内容包含:

实例对象
isa
成员变量信息

二. 类对象

NSObject *object1 = [[NSObject alloc] init];
NSObject *object2 = [[NSObject alloc] init];
Class objectClass1 = [object1 class];
Class objectClass2 = [object2 class];
Class objectClass3 = [NSObject class];
Class objectClass4 = object_getClass(object1);//Runtime API
Class objectClass5 = object_getClass(object2);//Runtime API

objectClass1 ~ objectClass5都是NSObject的class对象(类对象).它们是同一个对象,每个类在内存中有且只有一个class对象.
类对象存放的内容包含:

类对象
isa
superclass
属性信息
对象方法信息
协议信息
成员变量信息
.............

class对象在内存中存储的信息主要包括:isa指针,superclass指针,类的属性信息(@property)、类的对象方法信息(instance method),类的协议信息(protocol)、类的成员变量信息(ivar).

三. 元类对象

获取一个类对象的元类对象的方法.

Class objectMetaClass = object_getClass([NSObject class]);//Runtime API
元类对象
isa
superclass
类方法信息
.............

objectMetaClassNSObject的meta-class对象(元类对象).每个类在内存中有且只有一个meta-class对象.
meta-class对象和class对象的内存结构是一样的,但是用途不一样,在内存中存储的信息主要包括:isa指针,superclass指针,类的类方法信息(class method).
补充:
查看Class是否为meta-class:

BOOL result = class_isMetaClass([NSObject class]);

以下代码获取的objectClass是class对象,并不是meta-class对象

Class objectClass = [[NSObject class] class];

objcget-Class和object-getClass区别

objc_getClass 传入字符串类名返回类对象. 传入字符串类名返回类对象. 传入字符串类名返回类对象.
object_getClass 传入实例对象返回类对象. 传入类对象返回元类对象. 传入元类对象返回还是元类对象

四. isa和superClass

1. isa
isa的指向关系图.png

①instance的isa指向class,当调用对象方法时,通过instance的isa找到class,最后找到对象方法的实现进行调用.
②class的isa指向meta-class,当调用类方法时,通过class的isa找到meta-class,最后找到类方法的实现进行调用.

2. superClass
类对象的指向关系.png

当Student的instance对象要调用Person的对象方法时,会先通过isa找到Student的class,然后通过superclass找到Person的class,最后找到对象方法的实现进行调用.


元类对象的指向关系.png

当Student的class要调用Person的类方法时,会先通过isa找到Student的meta-class,然后通过superclass找到Person的meta-class,最后找到类方法的实现进行调用.

3. 经典的isa和superclass图谱
经典图片.png
  1. isa总结
  • instance的isa都是指向class.
  • class的isa都是指向meta-class.
  • meta-class的isa指向基类的meta-class.
  1. superClass总结
  • class的superClass指向父类的class.
  • 如果没有父类,superClass指针为nil.
  • meta-class的superClass指向父类的meta-class.
  • 基类meta-class的superClass指向基类的class.
  1. instance的调用轨迹
  • isa找到class,方法不存在,就通过superclass找父类.
  1. class调用类方法的轨迹
  • isa找meta-class,方法不存在,就通过superclass找父类.
  • 基类的meta-class方法不存在,就通过superclass找基类的class,如果没有找到就是nil.
4. isa地址运算
isa.png
isa的MASK地址.png

从64bit开始,isa需要进行一次位运算,才能计算出真实地址,superClass存储的地址值,直接就是父类的地址值,不用做位运算.


一个对象完整的结构.png

实例对象里只有成员变量没有方法,为什么实例对象的方法要存在类对象里,原因是只要存一份就够了,实例对象会创建多个.

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

推荐阅读更多精彩内容