【iOS】—— 面向对象,Runtime,ARC等问题总结

对于暑假学习大多数是对之前学习的一个复习,在这里只做对之前学习欠缺知识的补充以及这些知识点涉及的一些问题,从问题入手学习。

文章目录

  • 面向对象
    • 1.一个NSObject对象占多少内存?
    • 2.对象的isa指针指向哪里?
    • 3.OC的类信息存放在哪里
      • 一、instance对象(实例对象)
      • 二、class对象(类对象)
      • 三、meta-class对象(元类对象)
      • 扩展
      • 结论
  • Runtime
    • 1.讲一下OC的消息机制
    • 2.消息转发机制流程
    • 3.什么是runtime,平时项目中有用到过吗?
      • 具体应用
    • 4.[self class]和[super class]
  • ARC在编译和运行时做了什么
    • 我应该如何看待 ARC ?它将 `retains/releases` 调用的代码放在哪了?
    • block 是如何在 ARC 中工作的?
    • ARC 速度上慢吗?
    • dealloc方法的执行流程
    • 为什么KVO和通知要显式调用dealloc

面向对象

1.一个NSObject对象占多少内存?

结论:一个NSObject对象占16个字节内存

我们来打印一下看看:

    NSLog(@"实际占用内存——%zu, 实际分配内存——%zu", class_getInstanceSize([NSObject class]), malloc_size((__bridge const void *)obj));

在这里插入图片描述
为什么实际占用和实际分配有区别呢?
我们来细看看,其实在之前学runtime的时候那篇博客其实已经能解释这个问题了,我们围绕这个问题再来细说说。

首先来看为什么第一个是8:
很简单,下面这张图就可以看懂:
请添加图片描述
就一句话,因为isa指针的大小是8,所以其实一个NSObject实际占了8。

接着说,那为什么实际又分配了16呢?

这是在alloc时调的方法,里面默认设置如果内存小于16,直接默认内存为16。

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

最少是16,如果超过了16,返回的值和调用getinstanceSIze返回的是一致的,这里计算好后需要调用calloc全部初始化0,而calloc会进行内存对齐,对齐为16的倍数,所以malloc_size返回16的倍数是正确的值。

我们再来继承一个别的类看看:

@interface Person : NSObject
@property (nonatomic, assign) int a;
@property (nonatomic, assign) int b;
@property (nonatomic, assign) int c;
@end
NSLog(@"实际占用内存——%zu, 实际分配内存——%zu", class_getInstanceSize([Person class]), malloc_size((__bridge const void *)p));

还是一样打印它的结果:
在这里插入图片描述

每个内存是4,总共12,加上8,就是20,然后围绕8进行内存对齐即可。这个32就还是上面alloc那个方法,对齐到16的倍数就好。

2.对象的isa指针指向哪里?

来看这张图,基本就能懂了。
在这里插入图片描述
用自己的话解释一遍就是isa从实例走向该类再往父类指指到NSObject的时候,就指向类的元类,当指到最上面一层的时候,就指向自己。

3.OC的类信息存放在哪里

先说结论:

  • 1、对象方法、属性、成员变量、协议信息,存放在class对象中。
  • 2、类方法,存放在meta-class对象中。
  • 3、成员变量的具体值,存放在instance对象。

一、instance对象(实例对象)

int main(int argc, const char * argv[]) {@autoreleasepool {NSObject *object1 = [[NSObject alloc] init];NSObject *object2 = [[NSObject alloc] init];NSLog(@"%p %p", object1, object2);}return 0;
}

在这里插入图片描述
它们是不同的两个对象,分别占据着两块不同的内存。

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

  • isa指针
  • 其他成员变量

二、class对象(类对象)

int main(int argc, const char * argv[]) {@autoreleasepool {NSObject *object1 = [[NSObject alloc] init];NSObject *object2 = [[NSObject alloc] init];Class objectClass1 = [object1 class];Class objectClass2 = [object2 class];Class objectClass3 = object_getClass(object1);Class objectClass4 = object_getClass(object2);Class objectClass5 = [NSObject class];NSLog(@"%p %p", object1, object2);NSLog(@"%p %p %p %p %p", objectClass1, objectClass2, objectClass3, objectClass4, objectClass5);}return 0;
}

输出结果:
在这里插入图片描述
objectClass1 ~ objectClass5都是NSObject的class对象(类对象)
它们是同一个对象。每个类在内存中有且只有一个class对象。

class对象在内存中存储的信息主要包括:

  • isa指针
  • superclass指针
  • 类的属性信息(@property)
  • 类的对象方法信息(instance method)
  • 类的协议信息(protocol)
  • 类的成员变量信息(ivar)

三、meta-class对象(元类对象)

        NSObject *object1 = [[NSObject alloc] init];Class objectClass1 = [object1 class];Class objectMetaClass1 = object_getClass(objectClass1);NSObject *object2 = [[NSObject alloc] init];Class objectClass2 = [object2 class];Class objectMetaClass2 = object_getClass(objectClass2);NSLog(@"%p %p", objectMetaClass1, objectMetaClass2);

在这里插入图片描述
objectMetaClass是NSObject的meta-class对象(元类对象)
每个类在内存中有且只有一个meta-class对象

注意:meta-class对象和class对象的内存结构是一样的,但是用途不一样。

在内存中存储的信息主要包括:

  • isa指针
  • superclass指针
  • 类的类方法信息(class method)

扩展

1、Class objc_getClass(const char *aClassName)

  • 传入字符串类名
  • 返回对应的类对象

2、Class object_getClass(id obj)

  • 传入的obj可能是instance对象、class对象、meta-class对象
  • 返回值
    • a) 如果是instance对象,返回class对象
    • b) 如果是class对象,返回meta-class对象
    • c) 如果是meta-class对象,返回NSObject(基类)的meta-class对象

3、- (Class)class、+ (Class)class

  • 返回的就是类对象

结论

OC的类信息存放在哪里?

  • 1、对象方法、属性、成员变量、协议信息,存放在class对象中。
  • 2、类方法,存放在meta-class对象中。
  • 3、成员变量的具体值,存放在instance对象。

Runtime

1.讲一下OC的消息机制

OC对象调用方法在编译阶段不知道具体的方法在哪里,是在运行的过程中,向对象发送消息,通过对象得到函数地址,调用函数,如果没有找到,则抛出异常。
OC中方法调用,其实都是转成了objc_msgSend函数的调用, 给receiver 【方法调用者】 发送了一条消息 【selector 方法名】。

objc_msgSend 底层有3大阶段:

  • OC 消息发送
  • 动态方法解析
  • 消息转发

每个对象都有一个指向所属类的指针isa。通过该指针,对象可以找到它所属的类,也就找到了其全部父类,如下图所示:请添加图片描述

当向一个对象发送消息时,objc_msgSend方法根据对象的isa指针找到对象的类,然后在类的调度表(dispatchtable)中查找selector。如果无法找到selector,objc_msgSend通过指向父类的指针找到父类,并在父类的调度表(dispatchtable)中查找selector,以此类推直到NSObject类。一旦查找到selector,objc_msgSend方法根据调度表的内存地址调用该实现。
通过这种方式,message与方法的真正实现在执行阶段才绑定。

2.消息转发机制流程

消息转发机制大致可分为三个步骤:

  • 动态方法解析
  • 备援接收者
  • 完整消息转发
    请添加图片描述

3.什么是runtime,平时项目中有用到过吗?

  • OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
  • OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动 态性相关的函数
  • 平时编写的OC代码,底层都是转换成了Runtime API进行调用

具体应用

  • 利用关联对象(AssociatedObject)给分类添加属性
  • 遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动归档解档)
  • 交换方法实现(交换系统的方法)
  • 利用消息转发机制解决方法找不到的异常问题

4.[self class]和[super class]

在研究这个问题之前,我们先来打印这两个结果看看(多打印一个):

NSLog(@"%@ %@ %@", [self class], [super class], [self superclass]);

输出结果:
在这里插入图片描述
其实结果和我想象的有些不一样,我们来研究一下为什么:

简单来说,self和super都是指向当前实例的,不同的是,[self class]会在当前类的方法列表中去找class这个方法,[super class]会直接开始在当前类的父类中去找calss这个方法,两者在找不到的时候,都会继续向祖先类查询class方法,最终到NSObject类。那么问题来了,由于我们在Person中都没有去重写class这个方法,最终自然都会去执行NSObject中的class方法,结果也自然应该是一样的。至于为什么是Person,我们可以看看NSObject中class的实现:

-(Class)class { return object_getClass(self); 
}

这就说的通了,返回的都是self的类型,self此处正好就是Person,因此结果就会输出Person。

ARC在编译和运行时做了什么

chat:
在编译期,ARC会根据代码的语法和规则进行静态分析,确定每个对象的生命周期,并在适当的位置插入retain、release和autorelease等内存管理方法的调用。这样,在编译后的代码中,就会自动包含了正确的内存管理操作。

在运行期,ARC会跟踪对象的引用计数,并在对象不再被使用时自动释放其内存。当一个对象的引用计数减为0时,ARC会自动调用dealloc方法来释放对象占用的内存,并且会自动处理对象之间的循环引用问题。

需要注意的是,ARC只负责管理Objective-C对象的内存,对于Core Foundation框架中的C类型对象(如CFArrayRef、CFStringRef等),仍然需要手动管理内存。

总结起来,ARC在编译期通过静态分析插入合适的内存管理代码,而在运行期跟踪对象的引用计数并自动释放内存,从而简化了开发者对内存管理的工作。

除了会自动调用“保留”与“释放”方法外,使用ARC还有其他好处,它可以执行一些手工操作很难甚至无法完成的优化。
在编译期,ARC会把能够相互抵消的retain、release、autorelease操作约简。如果发现在同一个对象上执行了多次“保留”与“释放”操作,那么ARC有时可以成对的移除这两个操作。ARC会分析对象的生存期需求,并在编译时自动插入适当的内存管理方法调用的代码,而不需要你记住何时使用retain、release、autorelease方法。编译器还会为你生成合适的dealloc方法。
将内存管理交由编译器和运行期组件来做,可以使代码得到多种优化。比如,ARC可以在运行期检测到autorelease后面跟随retain这一对多余的操作。为了优化代码,在方法中返回自动释放的对象时,会执行一个特殊函数。

我应该如何看待 ARC ?它将 retains/releases 调用的代码放在哪了?

对于如何看待ARC,以下是一些观点:

  • 简化内存管理:ARC使开发人员无需手动管理内存,减少了内存泄漏和野指针等常见错误的风险。这使得开发过程更加简单和高效。
  • 自动释放对象:ARC会自动在对象不再被使用时释放它们,从而减少了手动调用release方法的需要。这样可以减少代码中的冗余,并提高开发速度。
  • 引用循环问题:尽管ARC可以自动处理大部分内存管理任务,但它并不能解决所有情况下的引用循环问题。在存在强引用循环的情况下,需要使用弱引用或无主引用来打破循环。

关于ARC将retains和releases调用的代码放在哪里,这是由编译器自动生成的。在ARC中,编译器会根据代码的语义和上下文,在适当的位置插入引用计数操作。这些操作通常是隐藏的,不需要开发人员显式地编写或管理。

block 是如何在 ARC 中工作的?

在ARC下,编译器会根据情况自动将栈上的block复制到堆上,比如block作为函数返回值时,这样你就不必再调用Block Copy。
需要注意的一件事是,在ARC下,NSString * __block myString这样写的话,block会对NSString对象强引用,而不是造成悬垂指针问题。如果你要和MRC保持一致,请使用__block NSString * __unsafe_unretained myString或(更好的是)使用__block NSString * __weak myString

ARC 速度上慢吗?

不。编译器有效地消除了许多无关的retain/release调用,并且已经投入了大量精力来加速 Objective-C 运行时。特别的是,当方法的调用者是ARC代码时,常见的 “return a retain/autoreleased object” 模式要快很多,并且实际上并不将对象放入自动释放池中。

dealloc方法的执行流程

在这里插入图片描述
对象的引用计数为0时会执行dealloc函数,调用栈如下:
dealloc->_objc_rootDealloc->object_dispose->objc_destructInstance
objc_destructInstance函数内部会依次调用c++析构函数object_cxxDestruct、关联对象析构函数_object_remove_assocations、弱引用析构函数clearDeallocating

为什么KVO和通知要显式调用dealloc

在Objective-C中,通知(Notifications)和键值观察(Key-Value Observing,KVO)都涉及到对象之间的观察和通信。当一个对象注册为通知的观察者或者添加了KVO观察者时,它需要在适当的时候取消观察,以避免潜在的内存泄漏。

显式调用dealloc方法是一种常见的方式来取消观察。当一个对象被释放时,它的dealloc方法会被调用,这是一个对象生命周期结束的时机。在dealloc方法中,你可以取消对通知的观察或者移除KVO观察者。

以下是关于为什么要显式调用dealloc的一些原因:

  • 内存管理:通过取消观察,可以确保不再持有已释放对象的引用,从而避免内存泄漏。如果没有正确地取消观察,观察者对象可能会继续存在,并且仍然保持对已释放对象的引用,导致内存泄漏。
  • 避免崩溃:如果一个已释放的对象仍然是通知的观察者或者KVO观察者,并且通知或者KVO事件发生时尝试访问该对象,就会导致崩溃。通过在dealloc方法中取消观察,可以避免这种情况发生。

需要注意的是,在ARC(Automatic Reference Counting)环境下,dealloc方法会被自动插入并处理内存管理。因此,你不需要手动调用dealloc来释放对象。但是,你仍然需要在适当的时候取消观察和移除KVO观察者,以确保正确的内存管理和避免潜在的问题。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/3666.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

诚迈科技子公司智达诚远精耕智能驾驶,为商用落地注入创新力量

近期&#xff0c;工业和信息化部副部长辛国斌在新闻发布会上表示&#xff0c;将启动智能网联汽车准入和上路通行试点&#xff0c;组织开展城市级“车路云一体化”示范应用&#xff0c;将支持L3级及更高级别的自动驾驶功能商业化应用。根据工信部最新消息&#xff0c;《智能网联…

实际上手体验maven面对冲突Jar包的加载规则 | 京东云技术团队

一、问题背景 相信大家在日常的开发过程中都遇到过Jar包冲突的问题&#xff0c;emm&#xff0c;在最近处理业务需求时我也遇到了不同版本jar包冲突导致项目加载出错的问题。主要是一个完整的项目会不可避免的使用第三方的Jar包来实现功能开发&#xff0c;各种第三方包之间可能…

Python 3 拷贝、浅拷贝、直接引用

诸神缄默不语-个人CSDN博文目录 复杂的以后再补。 总的来说&#xff0c;像常数、字符串这种比较简单的变量无所谓&#xff0c;但是对于一些复杂对象&#xff08;比如list等&#xff09;&#xff0c;如果直接使ba&#xff0c;相当于直接把a的路径给了b&#xff0c;b这个对象的…

day35-Postman/ajax

0目录 1.postman 2.ajax 1.Postman 1.1 定义&#xff1a;postman用于测试http协议接口&#xff0c;无论是开发还是测试人员 1.2 Servlet中的doGet&#xff08;&#xff09;/doPost…

建造者模式-复杂对象的组装与创建

生产一辆车&#xff0c;主要有以下步骤&#xff1a;安装骨架、安装发动机及安装轮胎。这些步骤有指定的执行顺序&#xff0c;步骤缺一不可。 图 传统方案 传统方案存在的问题&#xff1a; 传参不便&#xff0c;虽可在构造函数那传参&#xff0c;但是传参时需要注意参数顺序等…

出租屋智能电表系统

随着科技的不断发展&#xff0c;智能化逐渐成为人们生活中不可或缺的一部分。在房屋租赁市场中&#xff0c;智能电表系统成为越来越多出租屋的标配&#xff0c;为房东和租户带来了便捷和安全。本文将从以下几个方面介绍出租屋智能电表系统的特点和优势。 一、出租屋智能电表系统…

LCD-STM32液晶显示中英文-(7.字模及显示原理)

目录 字模介绍 什么是字模 字模的构成 字模显示原理 字模制作 如何制作字模 字模寻址公式 存储字模文件 字模介绍 什么是字模 有了编码&#xff0c;我们就能在计算机中处理、存储字符了&#xff0c;但是如果计算机处理完字符后直接以编码的形式输出&#xff0c;人类将难…

Flutter:网络图像缓存插件——cached_network_image

前言 为什么要使用这个插件&#xff0c;有什么用呢&#xff1f;毕竟官方提供了Image.network来进行网络图片加载 Image.network和CachedNetworkImage都可以用于在Flutter中加载网络图片&#xff0c;但它们之间有一些区别。 Image.network是Flutter核心库提供的一个构造函数&…

Java性能优化-测试try-catch放在循环内和外的性能对比与业务区别

场景 Java中使用JMH(Java Microbenchmark Harness 微基准测试框架)进行性能测试和优化&#xff1a; Java中使用JMH(Java Microbenchmark Harness 微基准测试框架)进行性能测试和优化_霸道流氓气质的博客-CSDN博客 使用如上方式测试Java中try-catch放在循环内和循环外是否有性…

Unity游戏源码分享-Unity手游射击横版游戏

Unity游戏源码分享-Unity手游射击横版游戏 战斗场景 项目地址&#xff1a; https://download.csdn.net/download/Highning0007/88050256

实训笔记7.18

实训笔记7.18 7.18一、座右铭二、Hadoop大数据技术 大数据软件一般都要求7*24小时不宕机三、Hadoop的组成3.1 HDFS3.2 MapReduce3.3 YARN3.4 Hadoop Common 四、Hadoop生态圈五、Hadoop的安装问题5.1 Hadoop的本地安装模式-基本不用5.2 Hadoop的伪分布安装模式5.3 Hadoop的完全…

MySQL(十):MySQL语法-进阶

MySQL语法-进阶 数据类型Text 类型Number 类型Date 类型 ASALTER TABLEconcat、group_concatSQL注入阻止SQL注入方案一方案二方案三 HAVING 子句临时表正则表达式获取服务器元数据事务导出数据导出数据导出表作为原始数导出SQL格式的数据 导入数据解决无法导入问题使用 LOAD DA…

解密:GPT-4框架与训练过程,数据集组成,并行性的策略,专家权衡,推理权衡等细节内容

大家好&#xff0c;我是微学AI&#xff0c;今天给大家解密一下GPT-4框架与训练过程&#xff0c;数据集组成&#xff0c;并行性的策略&#xff0c;专家权衡&#xff0c;推理权衡等细节内容。2023年3月14日&#xff0c;OpenAI发布GPT-4&#xff0c;然而GPT-4的框架没有公开&#…

GAMES101笔记 Lecture11 Geometry 2(Curces and Surfaces)

目录 Explicit Representations in Computer Graphics(计算机图形学中的显式几何表示)Point Cloud(点云)Polygon Mesh(多边形网格)The Wavefront Object File(.obj) Format(OBJ格式文件) Curves(曲线)Bezier Curves(贝塞尔曲线)Defining Cubic Bezier Curve With Tangents(定义…

【java】对ArrayList中的元素进行排序的几种方式

对ArrayList中的元素进行排序的几种方式 一、使用Collections工具类 1、对基本类型排序 通过Collections.sort()对基本类型排序默认是以升序排序 // 1.Collections.sort()默认按照升序排序 List<Integer> integerList new ArrayList<>(); Collections.addAll(…

访问学者面试申请如何应对?

作为一个学者面试申请者&#xff0c;面对这一重要机会&#xff0c;我们需要认真准备并采取适当的应对策略。下面知识人网小编将提供一些建议&#xff0c;帮助你在面试中取得良好的表现。 首先&#xff0c;在准备阶段&#xff0c;你应该研究并了解申请机构的背景和研究方向。了解…

CentOS7中安装docker并配置阿里云加速器

文章目录 一、docker的安装二、docker的卸载三、配置加速器四、docker-compose安装五、docker-compose卸载六、docker-compose相关命令七、常用shell组合 一、docker的安装 参考&#xff1a;https://docs.docker.com/engine/install/centos 本文内容是基于&#xff1a;CentOS L…

css 3个元素行排列,前2个元素靠左,第三个元素靠右

上效果&#xff1a; 实现方式&#xff1a; display:flex &#xff0c; 行排列&#xff0c;默认靠左对齐&#xff0c; 然后让第三个元素自动占满剩余的空间&#xff1a;flex-grow:1&#xff0c;text-align:end // wxml <!-- 支付方式--><view class"payment_…

Docker 安装 MongoDB开启认证,创建只读用户权限。

创建带认证的mongdb容器 docker run -itd --name mongo -p 27017:27017 mongo --auth --auth 就是开启mongodb权限认证。如果不加 --auth 则是无权限认证&#xff0c;连接成功后任何用户都可以对数据库进行读写操作。 进入容器并创建用户 docker run -itd --name mongo -p 27…

自定义实现list及其功能

#pragma once #include <iostream> #include <assert.h> using namespace std;namespace test {//******************************设置结点******************************template<class T>struct list_node{T _data;list_node<T>* _next;list_node&l…