Object-C的底层都是通过C/C++来实现的,所以OC中的对象也会转化成C/C++中的某一个数据结构,

我们在终端里通过指令

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

将oc代码转化为c++代码,我们可以看到NSObject的底层结构是:

struct NSObject_IMPL {Class isa;
};

Class是一个指向对象的结构体指针

typedef struct objc_class *Class;

所以NSObject最终会转化成一个结构体,内部只有一个指向对象的结构体指针

所以NSObject对象只会使用8个字节的内存空间来存储指针(当然 实际上给它分配了16个内存空间)

NSLog(@"%zd",class_getInstanceSize([NSObject class])); //实例对象的成员所占用的大小8 (实际使用的) 

NSLog(@"%zd",malloc_size((__bridge const void *)(obj))); //整个结构体占用的是16(实际分配的)

同时,通过阅读源码我们得知,当创建的对象分配的内存空间小于16个字节的时候  系统都会分配16个字节的空间  这属于是苹果规定。
    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;}

如果有一个student类继承了object并且有俩个int属性,那么student所占用的内存是多少呢?

student实际占用内存为16字节,系统分配的内存也是16字节。

假设有个person继承NSObject,student继承person,那么person和student各占用多少内存呢?

最终通过打印我们发现,person,实际占用16,系统分配16,student实际占用16,系统分配16.

为什么?person实际占用16??int 4个字节 isa 8个字节 应该是12个字节啊?这就涉及到了前面写到的结构体内存对齐了。

对象的分类

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

1.实例对象,就是通过类alloc出来的对象,每次调用alloc都会生成一个新的实例对象

object1 和 object2 就是两个实例对象

实例对象在内存中存储的信息包括:

1、isa指针 (其实isa也算是对象的成员变量  也就是说实例对象内部只包含自己的成员变量)

2、其他成员变量(这里是存储成员变量的具体值)

2、类对象(class)

objectClass1-5 都是NSObject的类对象 ,因为每个类在内存中有且只有一个类对象  所以上面五个类对象其实是同一个对象

类对象在内存中存储的信息主要有:

1、isa指针

2、superclass指针

3、成员变量(这里的成员变量只是描述性的  比如有哪些变量 是什么类型的  并不是实例对象的具体变量值)

4、类的对象方法(-号开头的方法)

5、类的协议信息和属性信息

类对象的本质结构↓↓↓

3、元类对象(meta-class)

objectMetaClass就是NSObject的元类对象,元类对象也是每个类在内存中有且只有一个,元类对象和类对象在结构上非常相似。

元类对象在内存中春初的主要信息有:

1、isa指针

2、superclass指针

3、类方法(+号开头的方法)

我们看到通过object_getclass方法即能获得元类对象 也能获得类对象  通过查看源码我们可以得知object_getclass会判断传进来的参数是类对象还是实例对象  如果是实例对象则返回类对象  如果传进来的是类对象则返回元类对象

我们也可以通过下面的函数来判断对象是不是元类对象

也就是说通过alloc创建的是实例对象  通过object_getclass(类对象)创建的是元类对象  其他对象则是类对象  但是类对象和元类对象有且只有一个

三类对象中 都含有isa指针,那么这个isa指针指向什么?

实例对象的isa指向类对象  类对象的isa指向元类对象  元类对象的isa指向基类的元类对象

正是通过isa指针 才让三种对象产生关联

比如说,一个实例对象想调用对象方法  但是对象方法存放在类对象中  那么就是通过isa找到对象方法再进行调用

同理  当调用类方法的时候  类方法是存放在元类对象中的  类对象通过isa指针找到元类对象 读取类方法列表中的类方法进行调用

superclass指针

在类对象和元类对象中都有一个superclass指针,其实这两种对象中的superclass指针作用类似,都是指向父类对象

类对象中的superclass指针:

比如现在有一个Person对象继承自NSObject  有一个Student继承自Person  当studen的实例对象调用对象方法的时候,首先实例对象会根据自己的isa指针去类对象中找有没有对应的方法  没有的话类对象会根据自己的superclass指针去父类的类对象中去查找(也就是student的类对象根据superclass指针去Person的类对象中去查找有没有对应的对象方法  再没有的话Person的类对象会根据自己的superclass指针去NSObject的类对象中去寻找 寻找到基类在没有对应方法的话就会报方法找不到的错误)

而元类对象中的superclass指针也是指引类对象去父类对象中寻找对应的类方法:

按照上面的例子,Student这个类 想调用一个类方法,首先是Student的类对象 根据isa指针去Student的元类对象中查找有没有对应的类方法  没有的话Student的元类对象会根据自己的superclass指针去父类的元类对象(也就是Person的元类对象)中查找有没有对应的类方法,在没有的话Person的元类对象再根据自己的superclass指针去NSObject的元类对象中寻找 有的话进行调用 没有的话NSObject的元类对象会根据superclass指针去NSObject的类对象中去寻找是否有相同名称的对象方法(这个地方下面会具体讲到为什么基类的superclass指针会指向对应的类对象)

关于上面提到的为什么基类的superclass指针为什么在找不到方法的时候会指向基类的类对象 也就是为什么没有找到对应的类方法的情况下却可以调用同名对象方法?

关于这一点我们通过代码来验证:

首先我们新建一个NSObject的分类,在.h文件中声明一个test的类方法,但在.m文件中并未实现这个类方法  而是实现了同名的对象方法()

#import "NSObject+Test.h"@implementation NSObject (Test)//+ (void)test
//{
//    NSLog(@"+[NSObject test] - %p", self);
//}- (void)test
{NSLog(@"-[NSObject test] - %p", self);
}@end

我们调用类方法发现,及时没有对应的类方法,程序也可以正常运行,并且成功调用了同名的对象方法:

假如我们在m文件没有实现同名test的对象方法,那么程序会报错的:

+[NSObject test]: unrecognized selector sent to class 0x7fffaddd7140

关于在h文件中有类方法的声明,这个是没有影响的 因为没有这个声明的话程序根本跑不起来  我们关注的点是基类的superclass指针为什么在找不到方法的时候会指向基类的类对象寻找同名的对象方法

比如我们在h文件中声明了test的对象方法  m文件没有实现test方法 同样会报unrecognized错  这就是因为基类的对象方法中找不到方法后直接返回空值  而不是像类方法一样从元类对象找不到再去到类对象找同名对象方法

关于基类的superclass指针为什么在找不到方法的时候会指向基类的类对象,这是因为oc在调用方法的时候实际上是转换为c/c++去底层实现的  但是c/c++的底层实现并没有区分类方法还是对象方法 也就是没有区分+-号

比如

[NSObject test];

实际上是转换为了

objc_msgSend([NSObject class], @selector(test))

没有区分+-号  所以在基类元类对象没有找到对应的类方法后回去基类的类对象中查看是否有同名的对象方法  有的话就调用  再没有的话就报错了

NSObject本质揭秘

查看OC对象占用字节数

转载于:https://www.cnblogs.com/gaoxiaoniu/p/10685238.html

OC对象的本质及分类相关推荐

  1. iOS底层原理总结 - OC对象的本质

    苹果官方文档 The Objective-C language defers as many decisions as it can from compile time and link time t ...

  2. 1 OC 对象的本质(一个NSObject 对象占用的内存大小)

    1 前言 目录 1 前言 2 一个NSObject占用多少内存 3 为什么呢 ? 4 如何在内存中看呢? OC 的面向对象都是基于C/C++ 的数据结构实现的 结构体 2 clang 命令转换成c++ ...

  3. 02.OC对象的本质

    文章目录 @[toc] 1.instance(实例对象) 2. class(类对象) 3.meta-class(元类对象) 4.注意 5. objc_getClass & object_get ...

  4. 2 OC 对象的本质(一个Student 占用的内存大小)

    一 Student 占用的内存空间 补充: 1 成员变量占用字节的大小: 2 内存对齐的规则:结构体的内存大小必须是最大成员变量的内存的倍数. 一个 Student 类,继承自NSObject,有两个 ...

  5. MJiOS底层笔记--OC对象本质

    本文属笔记性质,主要针对自己理解不太透彻的地方进行记录. 推荐系统直接学习小码哥iOS底层原理班---MJ老师的课确实不错,强推一波. OC对象本质 基于C与C++结构体实现 OC语言如何被编译器编译 ...

  6. 视频教程-iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-iOS

    iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化 小码哥教育CEO,曾开发了2个iOS的流行开源框架(MJRefresh.MJExtension),目前在国内的使用率非常高. 李 ...

  7. iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-李明杰-专题视频课程...

    iOS底层原理班(下)/OC对象/关联对象/多线程/内存管理/性能优化-236人已学习 课程介绍         得遇名师,突飞猛进!iOS培训王者MJ(李明杰)老师精心研发,iOS进阶课程,实用技术 ...

  8. 【OC底层】OC对象本质,如 isa, super-class

    Objective-C的本质 1.我们编写的Objective-C,底层现实都是C/C++,代码生成步骤如下: 2.在OC中的所有面向对象的实现,都是基于C/C++的数据结构实现的 3.将Object ...

  9. 小码哥iOS学习笔记第二天: OC对象的分类

    Objective-C中的对象, 简称OC对象, 主要可以分为3种 instance对象(实例对象) class对象(类对象) meta-class对象(元类对象) 一.instance instan ...

最新文章

  1. java 编写小工具 尝试 学习(四)
  2. 盘点:弱电施工过程中的电线电缆36计
  3. 为什么小程序裂变,最终都变成了利益驱动?
  4. 【转】 python socket向百度发送http长连接请求 并做搜索
  5. 进程间通信 - 剪贴板实现
  6. Shell 学习笔记之运算符
  7. java 播放.pcm文件,java播发PCM文件
  8. 【探路者】第五周立会报告1(总第27次)
  9. 日志异常处理-spring aop注解
  10. 查看Python第三方库的帮助文档
  11. mysql登陆案例_Mysql用户登陆验证过程 案例
  12. 网站分析平台:是选择百度统计,还是 Google Analytics 呢?
  13. 怎么用python画房子_python绘图作业:使用pygame库画房子
  14. 三菱,实现面向家庭剧院的液晶投影仪
  15. android短信发不了图片,手机发不出短信怎么办?-安卓手机发不出短信的解决方法 - 河东软件园...
  16. xxxx is not translated in zh-rCN, zh-rTW
  17. UGUI IPointerUpHandler事件失灵
  18. 世界杯优化算法及其Python实现
  19. 爱情日记(2005年3月)
  20. python: UserWarning: mkl-service package failed to import, therefore Intel(R) MKL initialization问题解决

热门文章

  1. C/C++中涉及存储方式的关键字:auto,static,register,extern2009-01-22 11:23auto关键字:
  2. 【CyberSecurityLearning 69】反序列化漏洞
  3. setup.s 解读——Linux-0.11 剖析笔记(三)
  4. 【三分钟刷一题力扣】移除元素
  5. 插值查找(按比例查找)
  6. LIVE555再学习 -- VLC搭建RTSP服务器(转)
  7. 元器件基础知识--排阻命名
  8. Mysql:kill命令之mysql杀死指定的进程(批操作)
  9. c语言ascii码表数字,求教!我想显示数字但是现在显示的却是数字在ASCII码中对应的符...
  10. 又掌握了一项新技能 - 断点调试 Gradle 插件