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

news2024/11/29 4:30:59

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

文章目录

  • 面向对象
    • 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.coloradmin.cn/o/763588.html

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

相关文章

PLSQL编程

1.概念和目的 1.1. 什么是PL/SQL? PL/SQL&#xff08;Procedure Language/SQL&#xff09; 是Oracle对sql语言的过程化扩展 (类似于Basic)&#xff1b; 指在SQL命令语言中增加了过程处理语句&#xff08;如分支、循环等&#xff09;&#xff0c;使SQL语言具有过程处理能力。…

Spring @Autowired 注解原理

Spring Autowired 注解原理 1.Autowired 使用 ComponentScan("org.example.bean") public class AnnoContextDemo {Autowiredprivate User user;public static void main(String[] args) {AnnotationConfigApplicationContext context new AnnotationConfigApplic…

Ultipa嬴图数据库 | 深圳国际金融科技大赛圆满落幕

2023年7月13日&#xff0c;由深圳市地方金融监督管理局、深圳市福田区人民政府、深圳市南山区人民政府指导&#xff0c;招商局金融科技有限公司主办的2022深圳国际金融科技大赛总决赛在福田区圆满落幕。经过从初赛到决赛&#xff0c;共计103个项目的激烈角逐&#xff0c;Ultipa…

Unity视角拉近时物体缺失的问题处理

在Unity的开发过程中&#xff0c;我们可能会遇到以下情况&#xff1a; 就是在场景的不断编辑中&#xff0c;突然又一次打开场景&#xff0c;再拉近或拉远场景视角时&#xff0c;会出现场景中的对象会显示不全的问题。 出现了这样的情况会让场景的预览很不友好。 出现这个问题的…

【006】面向 6G 的深度图像语义通信模型

摘要 目前的语义通信模型在处理图像数据方面仍有可改善的部分&#xff0c;包括有效的图像语义编解码、高效的语义模型训练和精准的图像语义评估。为此&#xff0c;提出了一种深度图像语义通信&#xff08;DeepISC&#xff09;模型。首先采用基于 vision transformer 的自编码器…

数字IC后端设计实现中的Post-mask ECO应该怎么做?

在数字IC后端设计实现中&#xff0c;我们经常会涉及到芯片需要做Function ECO。常见的Function ECO可以分为pre mask ECO和post mask ECO两种。因此&#xff0c;作为一个数字IC后端工程师&#xff0c;必须熟练掌握这两种Function ECO的实现流程及其实现技巧。 两者的区别在于&…

栈和队列【数据结构】

1、栈 &#xff08;1&#xff09;Stack.h #pragma once #include <stdio.h> #include <assert.h> #include <stdlib.h> #include <stdbool.h>typedef int STDataType;typedef struct Stack {STDataType* a;int top;int capacity; }ST;void STInit(ST*…

[JVM] 5. 运行时数据区(2)-- 程序计数器(Program Counter Register)

一、概述 JVM中的程序计数器&#xff08;Program Counter Register&#xff09;是对物理PC寄存器的一种抽象模拟。它是一块很小的内存空间&#xff0c;几乎可以忽略不记。也是运行速度最快的存储区域。在 JVM 规范中&#xff0c;每个线程都有它自己的程序计数器&#xff0c;是…

.nvmrc无效

背景 既然你已经使用了nvm那么他的功能我就不介绍了。但是使用场景我与开发小伙伴探讨了一下发现很多问题。你的nvm使用方法真的正确吗&#xff1f; 问题&#xff1a;假设现在有10个项目对应10个不同的node版本&#xff0c;你应该怎么来管理呢&#xff1f; 同学1&#xff1a; …

LT8619C是一款高性能HDMI转TTL/BT656/BT601/BT1120加2PORT LVDS,支持高达4K30HZ的分辨率。

LT8619C • 概述&#xff1a; Lontium的LT8619C是一款高性能的HDMI/双模式DP接收器芯片&#xff0c;符合HDMI 1.4规范&#xff0c;支持HDCP1.4解码&#xff0c;对HDMI的支持分辨率高达4Kx2K30Hz。TTL输出可支持RGB、BT656、BT1120&#xff0c;支持最多24位RGB或BT656/BT1120输…

动手学深度学习——多层感知机(原理解释+代码详解)

目录 一、多层感知机1. 隐藏层1.1 线性模型可能会出错1.2 在网络中加入隐藏层1.3 从线性到非线性1.4 通用近似定理 2. 激活函数2.1 ReLU函数2.2 sigmoid函数2.3 tanh函数 3. 小结 二、多层感知机的从零开始实现2.1 初始化模型参数2.2 激活函数2.3 模型2.4 损失函数2.5 训练 三、…

【数据结构刷题】消失的数字和轮转数组

目录 一.消失的数字 方法一:异或全部元素 方法二:利用等差数列求和-该数组全部元素之和。 二.轮转数组 题型1:实现一个函数&#xff0c;可以左旋字符串中的k个字符。 写法1:暴力求解 根据该题写出右旋转 写法2&#xff1a;三步旋转法(左逆序&#xff0c;右逆序&#xff0c;整体…

2023年7月字节前端青训营入营题目记录(大题)

前言&#xff1a; 不一定是完整的题目内容&#xff0c;但意思差不多是一个意思 1.实现一个url解析成对象的函数&#xff1a; function ParseParams(url: string): Record<string, any> {const paramsstr url.split("?")[1];const paramsArr paramsstr.spl…

IDEA中把导航栏的字体放大

IDEA中如何把导航栏的字体放大&#xff1f; 选择File--Settings--Appearance&#xff0c;找到下面的size,选择自己想要的字体大小后点击 OK 即可。 字体大了果然看的更舒服了~写代码都有动力了哈哈哈哈 服了~

MySQL基本语句

目录 一、MySQL数据库管理 查看数据库信息 查看数据库中的表信息use 数据库名 #切换到书库中 显示数据表的结构&#xff08;字段&#xff09; 二、SQL语句 1.创建新的数据库 2.创建新的表 3.增加&#xff08;insert&#xff09; 4.删除 4.1清空表 4.2删除表 5.修改…

SpringMVC的数据响应-直接回写json字符串

一般我们操作对象&#xff0c;将对象转变为json 这时导入json 转换工具的包 包1 包2-json数据绑定 包3 返回的就是json字符串你直接返回就行了 返回一个json格式的字符串 直接回写就加这个res.... 内部字符串要进行相应的转意 能够看到json字符串 能不能你封装对象&#xff0c…

jenkins+python+pytest+selenium 自动化执行脚本并发送报告

目录 安装jenkins jenkins 安装网址&#xff1a; 傻瓜式安装 配置环境 键path 或者随便填 构建后操作 在jenkins上展示html的报告&#xff0c;需要添加一个HTML Publisher plugin插件 查看报告显示丢失了css样式 加载css样式丢失解决&#xff1a;https://www.cnblogs.com/…

如何使用Dom4J解析XML文档

文章目录 XML解析的方式使用Dom4J解析XML文档Dom4J结合XPath解析XML 最近在手写MyBatis的源码&#xff0c;在写到XMLConfigBuilder的时候&#xff0c;其中要解析xml文件构建Configuration。在MyBatis的早期版本中使用了DOM4J来解析配置文件和映射文件。但是从3.x版本开始,MyBat…

数据结构——绪论

基本概念 数据&#xff1a;数据是信息的载体&#xff0c;对客观事物的字符表示。 数据元素&#xff1a;数据的基本单位&#xff0c;通常作为一个整体进行考虑和处理。 数据项&#xff1a; 一个数据元素由多个数据项组成&#xff0c;数据项是数据元素不可分割的最小单位。 数据…

流程图如何制作?几个流程图实用制作方法教给你

流程图如何制作&#xff1f;流程图是一种重要的图表类型&#xff0c;通常用于描绘系统、流程或程序的步骤和关系。它们在各种领域都有广泛的应用&#xff0c;包括工程、科学、商业和教育等。本文将介绍一些制作流程图的实用方法&#xff0c;以及一些快速、易于使用的工具。 制作…