内存分区,编译链接,ARCMRC,消息传递消息转发,对象的底层


前言

对第一周学习内容做个概括


提示:以下是本篇文章正文内容,下面案例可供参考

内存分区

内存分区
内存一共分为五大区域,栈区,堆区,全局区,常量区,代码区

栈区

由编译器自动分配释放,存放函数的参数值

堆区

允许程序在运行时动态的申请某个大小的内存空间,一般由程序员分配释放,若程序员不释放,程序结束时可能由OS回收。
和数据结构里面的堆不一样

全局区

全局变量和静态变量的存储是放在一起的,初始化的全局变量和静态变量在一块区域,未初始化的全局变量和未初始化的静态变量在相邻的另一块区域,程序结束后由系统释放。

文字常量区

常量字符串就放在这,它是只读的。程序结束后由系统释放。

程序代码区

存放程序的编译后的二进制代码,CPU执行的机器指令,并且是只读的。

程序在不同的阶段会在内存中占据不同的内存区域。

运行之前

运行之前,分为代码区,数据区,未初始化数据区。

运行之后

运行之后分为代码区,未初始化数据区,数据区,栈区,堆区

编译,链接

编译链接详细学习

编译的过程

  1. 预处理

这一步做了这几件事
宏替换:源代码中使用的宏定义会被替换为对应的#define内容
头文件引入:(#include,#import)
使用对应的.h文件内容替换这一行的内容,所以尽量减少头文件的#import,使用@class代替,把#import放到.m文件中
处理条件编译指令:(#if,#else,#endif)

  1. 词法分析

把原文件中的代码转化为特殊的标记流,源码被分为一个一个的单词,在行尾Loc中都标记出了源码所在的对应的原文件和具体行数,方便在报错的时候定位问题

  1. 语法分析

把此词法分析生成的标记流,解析成为一个抽象语法树,这里面的每一节点也都标记了其在源码中的位置

  1. 静态分析

把源码转化为抽象语法树后,编译器就可以对这个树进行处理。静态分析会对代码进行错误检测,如出现方法被调用但是未定义,定义但是未使用的变量等。
类型检查:clang做检查,检查程序是否发送正确的消息给正确的对象,是否在正确的值上调用了正常函数
其他分析:ObjCUnusedIVarsChecker.cpp检查是否有生成了但是从未使用的变量。ObjCSelfInitChecker.cpp是检查在 你的初始化方法中中调用 self 之前,是否已经调用[self initWith…]或[super init]了

  1. 生成中间代码和优化

LLVM有三种表示形式,他们本质上是等价的
text:文本格式
memory:内存格式
bitcode:二进制格式

  1. 汇编

汇编器将上一步生成的刻度的汇编代码转化为机器代码,最终产物是以.o结尾的目标文件。

链接

在编译的最后一步汇编结束后,就到了链接这一步

将上一步产生的目标文件和引用的静态库链接起来,最终生成可执行文件,链接解决了目标文件和库之间的链接

ARC,MRC

ARC的工作原理

在编译期干了什么

ARC会把能够相互抵消的retain、release、autorelease操作约简
ARC会分析对象的生存期需求,并在编译时自动插入适当的内存管理方法调用的代码
编译器还会为你生成合适的dealloc方法
将内存管理交由编译器和运行期组件来做,可以使代码得到多种优化

对象的底层

对象的底层详细学习

OC对象的本质就是结构体
struct NSObject_IMPL结构体事实上是一个Class类型的isa指针
objc_class是继承objc_object的
每个类的底层都会有一个Class类的isa指针。
Class底层是struct objc_class *类型,NSObject底层是struct objc_object结构体,id底层是struct objc_object *类型

struct objc_object {
    Class _Nonnull isa __attribute__((deprecated));
};

id底层实现是struct objc_object 类型,怪不得声明id类型变量时 后面不用再加""了,因为它定义的时候就定义为了一个Class的指针

SEL是struct objc_selector 类型
IMP是void (
)(void )函数指针类型

struct objc_class : objc_object {
    //...省略无关代码
    // Class ISA;  //ISA(从objc_object继承过来的)
    Class superclass;  //指向其父类
    cache_t cache;  //缓存
    class_data_bits_t bits;  //类的数据
    
	class_rw_t *data() const {
        return bits.data();
    }
    void setData(class_rw_t *newData) {
        bits.setData(newData);
    }
    //...省略无关代码
}  

所有对象都是以objc_object为模板继承过来的。
类是一个结构体,里面存放了isa、superClass、cache、bits等

isa指针保存着指向类对象的内存地址,类对象全局只有一个,因此每个类创建出来的对象都会默认有一个isa属性,保存类对象的地址,也就是class,通过class就可以查询到这个对象的属性和方法,协议

源码主要是初始化isa,两种方式:

通过cls初始化:
非nonpointer,存储着Class、Meta-Class对象的内存地址信息。
通过bits初始化:
nonpointer,进行一系列的初始化操作。

消息传递,消息转发

消息传递和消息转发详细学习

消息转发

iOS的消息转发是指当一个对象收到一个无法响应的消息时,其会通过多个方法转发该消息,直到能够响应为止
当消息接收者无法响应某个方法时,Objective-C消息传递机制会按照以下顺序进行转发

  1. 动态方法解析
  2. 备援接收者
  3. 完整消息转发

如果没有找到消息,该怎么处理呢?就需要消息转发。
消息转发机制大致可分为三个步骤:

  1. 动态方法解析
  2. 备援接受者
  3. 完整消息转发

请添加图片描述

消息传递

在 iOS 中,消息传递是指通过向对象发送消息并让其执行对应的方法,从而实现组件间的通信和协作。

在 Objective-C 中,消息传递是通过向对象发送消息来实现的。当一个对象收到消息时,Objective-C 运行时会根据消息所带的函数名(即 Selector),查找该对象所属的类中是否有对应的方法实现。如果找到,则运行时会执行该方法实现;如果没找到,则会先尝试进行动态方法解析,再尝试进行消息转发

在 Objective-C 中,消息直到运行时才绑定到方法实现上。编译器会将消息表达式转化为一个消息函数的调用。

IMP指针

IMP本质就是一个函数指针,这个被指向的函数包含一个接收消息的对象id,调用方法的SEL,以及一些方法参数,并返回一个id。因此我们可以通过SEL获得它所对应的IMP,在取得了函数指针之后,也就意味着我们取得了需要执行方法的代码入口,这样我们就可以像普通的C语言函数调用一样使用这个函数指针

IMP与SEL的区别与联系

SEL:类方法的指针,相当于一种编号,区别与IMP
IMP:函数指针,保存了方法的地址

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

山河丘壑

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值