OC语言学习记录

article/2025/11/6 11:10:08

目录

一、主要的OC的基本类型介绍

二、IOS属性

1、什么是属性

2、属性修饰符

A、读写性修饰符

B、原子性修饰符

C、assgin和weak

D、strong和copy

E、retain

三、iOS内存管理

1、什么是内存管理?

2、内存是怎么分配的,是分配在哪里的?

3、什么是引用计数

 4、MRC手动管理引用计数

5、ARC自动管理引用计数

7、Core Foundation 对象的内存管理

 四、参考文档


本文档是作者自学的总结文档,如有侵权,请联系作者,作者进行删除文档

一、主要的OC的基本类型介绍

OC的数据类型有:
字符串,浮点型,整型,布尔类型 而且OC的数组也分为可变数组和不可变数组 OC的词典也分为可变词典和不可变词典

二、IOS属性

1、什么是属性

属性是OC语言的一个机制,我们在OC中用@property来声明的一个属性,其实@property是一种语法糖,编译器会自动为你的实例变量生成getter方法和setter方法。

2、属性修饰符

A、读写性修饰符

(1)readwrite

表明这个属性是可读可写,是系统默认属性,系统为我们创建settergetter方法。

(2)readonly

表明这个属性只可读不能写,系统只为我们创建getter方法,不会创建setter方法。

B、原子性修饰符

(1)atomic

原子属性,线程安全但可能降低性能。

(2)nonatomic

非原子属性,提高性能但非线程安全。

C、assgin和weak

weak与assgin都表示了一种非持有关系,也不被称为弱引用,在调用时,不会增加引用对象的引用计数。weak在引用对象被销毁时,会被指向nil;而assgin不会被置为nil。

(1)assign

如果用assgin修饰对象,当assgin指向的对象被销毁时,assgin就会指向一块无效内存(变成悬空指针),如果这个时候你给assgin发送消息,程序就会发生崩溃,也有可能不会崩溃,这个取决于你发送消息时,那块内存还是否存在。

assgin一般修饰的基础数据类型(NSInteger,int,float,double,char,bool等),因为基础数据类型是被分配到栈上,栈的内存会由系统自动处理,所以不会造成悬空指针。

(2)weak

只用于修饰对象。

D、strong和copy

使用strong和copy是都会使引用对象引用计数+1。但是使用copy修饰的属性在某些情况下赋值的时候会创建对象的副本,就是深拷贝。strong是两个指针指向同一个内存地址,copy是在内存中拷贝一份对象,两个指针指向不同的内存地址。

(1)copy

copy修饰NSString、NSArray、block属性。我们以为copy只是深拷贝,其实不是的,系统在当源字符串为不可变类型时,你的属性copy其实就是进行浅拷贝,当源字符串为可变类型时,才进行深拷贝。但是我们建议在使用NSString属性的时候用copy,避免可变字符额修改导致一些非预期问题。

注:

1、对不可变对象执行copy操作,是浅拷贝(指针拷贝,内容相同),都指向同一边存储空间,源数据被修改,副本数据也会被修改

2、对不可变字符串执行mutCopy操作,是深拷贝(对象拷贝),两者指向不同的存储空间

3、可变字符串执行copy或mutCopy都是深拷贝

4、可变容器类执行copy或mutcopy或者不可变容器执行mutCopy都是不完全深拷贝,即只是容器对象指向不同的内存空间,内部的元素则指向同一个内存

5、可变数组执行copy(NSMutableArray执行copy后返回的NSArray),在使用过程回出现crash的问题

6、数组完全深拷贝需要执行initWithArray:copyItems: 方法 NSArray *deepCopy = [[NSArray alloc] initWithArray:array copyItems:YES];

(2)strong

strong修饰NSString、block以外的OC对象。

注:

strong与copy的区别:

(1)当原字符串是NSString时,由于是不可变字符串,所以,不管使用strong还是copy修饰,都是指向原来的对象,copy操作只是做了一次浅拷贝。

(2)当源字符串是NSMutableString时,strong只是将源字符串的引用计数加1,而copy则是对原字符串做了次深拷贝,从而生成了一个新的对象,并且copy的对象指向这个新对象。

所以,如果源字符串是NSMutableString的时候,使用strong只会增加引用计数。但是copy会执行一次深拷贝,会造成不必要的内存浪费。而如果原字符串是NSString时,strong和copy效果一样,就不会有这个问题。

但是,我们一般声明NSString时,也不希望它改变,所以一般情况下,建议使用copy,这样可以避免NSMutableString带来的错误。

strong与copy的区别

E、retain

retain修饰NSArray,NSDate。对应的setter方法。

与assign相对,我们要解决对象被其他对象引用后释放造成的问题,就要用retain来声明。retain声明后的对象会更改引用计数,那么每次被引用,引用计数都会+1,释放后就会-1,即使这个对象本身释放了,只要还有对象在引用它,就会持有,不会造成什么问题,只有当引用计数为0时,就被dealloc析构函数回收内存了。

IOS属性

IOS深拷贝和浅拷贝

三、iOS内存管理

1、什么是内存管理?

内存管理是指软件运行时对计算机内存资源的分配和使用的技术。其最主要的目的是如何高效、快速的分配,并且在适当的时候释放和回收内存资源。

2、内存是怎么分配的,是分配在哪里的?

在IOS中数据是存在堆和栈中的,然而我们的内存管理是管理堆上的内存,栈上的内存并不需要我们管理(计算机会自动回收)。

  • 基础数据类型(非OC对象)存储在栈上
  • OC对象存储在堆上
int a = 10; //栈
int b = 20; //栈
Car *c = [[Car alloc] init]; 

在内存中的表现形式如下:

3、什么是引用计数

引用计数是一个简单而有效的管理对象生命周期的方式。当我们创建一个新的对象的时候,它的引用计数为1,当有一个新的指针指向这个对象时,我们将其引用计数+1,当某个指针不在指向这个对象,我们将其引用计数-1,当对象的引用计数为0是,说明该对象不再被任何指针指向了,我们就可以将对象销毁,回收内存。如图所示。

 4、MRC手动管理引用计数

a、在MRC中增加引用计数都是需要自己手动释放的。

对象操作OC中对应的方法引用计数的变化
生成并持有对象alloc/new/copy/mutableCopy等+1
持有对象retain+1
释放对象release-1
废弃对象dealloc0

b、四则法则

  • 自己生成的对象,自己持有。
/** 自己生成并持有该对象*/id obj0 = [[NSObeject alloc] init];id obj1 = [NSObeject new];
  • 非自己生成的对象,自己也能持有。
/** 持有非自己生成的对象*/
id obj = [NSArray array]; // 非自己生成的对象,且该对象存在,但自己不持有[obj retain]; // 自己持有对象
  • 不在需要自己持有对象的时候,释放。
/** 不在需要自己持有的对象的时候,释放*/
id obj = [[NSObeject alloc] init]; // 此时持有对象[obj release]; // 释放对象
/** 指向对象的指针仍就被保留在obj这个变量中* 但对象已经释放,不可访问*/
  • 非自己持有的对象无需释放。
/** 非自己持有的对象无法释放*/
id obj = [NSArray array]; // 非自己生成的对象,且该对象存在,但自己不持有[obj release]; // ~~~此时将运行时crash 或编译器报error~~~ 非 ARC 下,调用该方法会导致编译器报 issues。此操作的行为是未定义的,可能会导致运行时 crash 或者其它未知行为

5、ARC自动管理引用计数

ARC其实也是基于引用计数,只是编译器在编译时期自动在已有代码中插入合适的内存管理代码(包括retain,release,copy,autorelease,autoreleasepool)以及在Runtime做一些优化。

ARC已经处理了大部分的引用计数,还有一小部分的引用计数需要自己管理,这一部分就是底层Core Foundation对象因为不在ARC的管理下,所以需要自己维护这些对象的引用计数。

所有权修饰符:

OC编程中为了处理对象,可将变量类型定义为id类型或各种对象类型。ARC中id类型和对象类型其类型必须附加所有权修饰符。

以下是四种所有权修饰符:

  • __strong
  • __weak
  • __unsafe_unretained
  • __autoreleasing

所有权修饰符和属性修饰符对应关系如下表:

所有权修饰符属性修饰符
__unsafe_unretainedassign
__unsafe_unretainedunsafe_unretained
__strongcopy
__strongstrong
__strongretain
__weakweak

a、__strong简介

__strong表示强引用,对应定义property时用到的是strong。当对象没有任何一个强引用指向他的时候,他才会被释放。如果声明引用时不加修饰符,那么引用将默认是强引用。当需要释放强引用指向的对象时,需要保证所有指向对象强引用置为nil。_strong修饰符是id类型和对象类型默认的所有权修饰符。

b、__weak简介

__weak表示弱引用,对应定义property时用的是weak。弱引用不会影响对象的释放,而当对象被释放时,所有指向他的弱引用都会自定被置为nil,这样可以防止野指针。使用__weak修饰的变量,即是使用注册到autoreleasepool(自动释放池)中的对象。__weak最常见的一个作用就是用来避免循环引用。

c、__unsafe_unretained简介

这个修饰符在定义property是对应的是unsafe_unretained。__unsafe_unretained修饰的指针纯粹只是指向对象,没有任何额外的操作,不会去持有对象使得对象retainCount+1。而在指向的对象被释放时依旧原原本本的指向原来的对象地址,不会被自动置为nil,所以会变成野指针。

d、__autoreleasing简介

将对象赋值给附有__autoreleasing修饰符的变量等同于MRC时调用对象的autorelease方法。 、6、ARC下的内存管理问题

a、过度使用block之后,无法解决循环引用问题。

b、遇到底层Core Foundation对象,需要自己手工管理他们的引用计数时,显得一筹莫展。

循环引用问题:

引用计数这种内存管理方式虽然很简单,但是有一个比较大的瑕疵,就是他不能很好的解决循环引用问题。如下图所示:

对象A和对象B,相互引用了对方作为自己的成员变量,只有当自己销毁时,才会将成员变量的引用计数-1。因为对象A的销毁依赖于对象B销毁,而对象B的销毁也依赖于对象A的销毁,这样就造成了我们称之为循环引用的问题,这两个对象即使在外界已经灭有任何指针能够访问到他们了,他们也无法被释放。

不止存在两对象循环引用的问题,多个对象依次持有对方,形成一个环状,也可以造成循环引用问题,而且在真实的编程环境中,环越大就越难被发现。如下图所示:

解决循环引用问题,主要有两种方式:

  • 主动断开循环引用

我明确知道这里存在循环引用,在合理的位置主动断开环中的一个引用,使得对象得以回收

  • 使用弱引用

弱引用虽然持有对象,但是并不增加引用计数,这样就避免了循环引用的产生。在IOS开发中,弱引用通常在delegate模式中使用。举个例子来说,两个ViewController A和B,ViewController A需要弹出一个ViewController B,让用户输入一些内容,当用户输入完成后,ViewController B需要将内容返回给ViewController A。这个时候,ViewController的delegate成员变量通常是一个弱引用,以避免这两个ViewController相互引用对方造成循环引用问题,如下图所示。

弱引用的实现原理就是这样,系统对于每一个弱引用的对象,都维护一个表来记录他所有的弱引用的指针地址。当一个对象的引用计数为0,系统就通过这张表,找到所有的弱引用指针,继而把他们都置成nil。

7、Core Foundation 对象的内存管理

底层的Core Foundation对象,在创建时大多以XxxCreateWithXxx这样的方式创建,例如:

// 创建一个 CFStringRef 对象
CFStringRef str= CFStringCreateWithCString(kCFAllocatorDefault, “hello world", kCFStringEncodingUTF8);// 创建一个 CTFontRef 对象
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL);

对于这些对象的引用计数的修改,要相应的使用 CFRetain 和 CFRelease 方法。如下所示:

// 创建一个 CTFontRef 对象
CTFontRef fontRef = CTFontCreateWithName((CFStringRef)@"ArialMT", fontSize, NULL);// 引用计数加 1
CFRetain(fontRef);// 引用计数减 1
CFRelease(fontRef);

对于CFRetain和CFRelease两个方法,可以与OC对象的retain和release方法等价。所以对于底层Core Foundation对象,我们用手工管理引用计数的办法就可以。

但是在ARC下,我们有时需要将一个Core Foundation对象转换成OC对象,需要引入bridge相关的关键字。 _bridge:只做类型转换,不修改相关对象的引用计数,原来的Core Foundation对象在不用时,需要调用CFRelease方法。 _bridge_retained:类型转换后,将相关对象的引用计数+1,原来的Core Foundation对象在不用时,需要调用CFRelease方法。 _bridge_transfer:类型转换后,将该对象的引用计数交给ARC管理,Core Foundation对象在不用时,不再需要调用CFRelease方法。

IOS内存管理

 四、参考文档

strong与copy的区别

IOS属性

IOS深拷贝和浅拷贝

IOS内存管理


http://chatgpt.dhexx.cn/article/iS6h9RC2.shtml

相关文章

OC语言--OC语言基础、类

一、OC语言基础 1>OC文件的扩展名为.m、.c; 2>OC语言完全兼容C语言,即OC语言中可以写C语言; 3>OC语言也可以写C语言,但是如果要写C语言,则OC文件扩展名为.mm; 4>OC语言是在C语言的基础上增加了最小层的面…

OC语言特性

一、分类 1.1做了那些事?(关键词:明私、分解、私公开化) 声明私有方法,分类头文件放到数组文件.m中,不对外暴露 分解体积庞大的类文件 功能复杂的按功能分类 framework私有方法公开化 1.2特点 &#x…

下载各版本cuda,下载以前版本的cuda

1.打开网址https://developer.nvidia.com/cuda-toolkit-archive 2.下载10.1版本就点击CUDA Toolkit 10.1 3.如图选择自己的版本。 4.打开这个网址就可下载。 5.cd到下载目录,执行sudo sh 文件名 指令 6.移动到continue按enter键确认 7.回复accept 8.不安装驱动 9.安…

Windows下显卡支持的cuda及配套GPU版本pytorch下载全流程(2022.03.27)

Windows下显卡支持的cuda及配套GPU版本pytorch下载全流程(2022.03.27) 一、更新电脑显卡驱动(以mx150为例) 1、打开英伟达驱动下载官网:https://www.nvidia.cn/geforce/drivers/ 2、直接下载自动更新程序&#xff0c…

NVIDIA CUDA和cuDNN显卡历代版本下载地址

NVIDIA显卡计算能力一览表 https://developer.nvidia.com/cuda-gpus Halcon图像库支持深度学习,GPU计算能力要求3.0以上 深度学习必备 先安装N卡的显卡驱动--->再安装cuda-->最后安装cuDNN 显卡驱动 1、NVIDIA官方 https://www.nvidia.cn/Download/index…

Windows 下载与安装CUDA和Pytorch【安装教程、深度学习】

参考链接:Windows 下安装 CUDA 和 Pytorch 跑深度学习 - 动手学深度学习v2_哔哩哔哩_bilibili 0.准备工作 请确保你是NVIDIA的显卡(不能是AMD、集成显卡) 1.下载CUDA 打开developer.nvidia.com/cuda-downloads,打开有点慢 选择…

CUDA下载,以及下载GPU版本的pytorch

一、下载anaconda 因为这步我之前就下好了,主要参考这个链接:史上最全最详细的Anaconda安装教程 二、下载CUDA 1.首先观察自己需要什么版本的CUDA,以及是否安装过CUDA 先cmd,输入命令 nvidia-smi结果如下,所以我们…

[cuda]cuda驱动版本对应+旧版本cuda下载网址

一、背景 本文用于记录在安装cuda时可能需要的网址,留作己用。 二、cuda、nvidia驱动版本对应关系 1. cuda需要的最低驱动版本 2. cuda与对应的驱动版本 三、旧版本cuda下载网址 所有版本的cuda下载地址如下: cuda Toolkit Archive.

下载和安装CUDA和Cudnn(图文详解)

Pytorch环境需要用到CUDA,所以我们要安装CUDA的驱动。这里我安装的是CUDA10.0,对应的Cudnn是7.4.1。 一、下载 CUDA10.0的官网地址,选择好自己所需版本,这里是我推荐的选择,然后点击Download。 cudnn官网地址&#x…

cuda 10.1 下载

最近发现百度到的关于CUDA10.1版本的安装的文章,给的链接已经是10.2版本的了。想找个10.1版本的有点艰难,不过其实官网有历史版本的下载入口的 进入这个地址https://developer.nvidia.com/cuda-toolkit-archive,可以下载旧版本的CUDA 不想去官…

cuda官网下载,本机显卡驱动与cuda、cudnn、pytorch、torchvision安装版本对应表,以及完全卸载CUDA方法

注:如果执行第一步没有找到nvidia-smi指令,重装本机NVIDIA驱动(Linux, Windows 标题中各项版本对应) 重装NVIDIA驱动,选择自己的显卡产品系列和产品编号, 如下图 GeForce RTX 30系列,产品是3080 Ti,系统windows 10 64…

CUDA的下载安装

大家好,下面将进行CUDA的下载安装,下载安装的详细步骤描述如下: (1)CUDA下载 https://download.csdn.net/download/qq_41104871/87462747 (2)CUDA安装 1、首先,需要解压缩下载好的CUDA压缩包,CUDA压缩包如下图所示: 2、解压后,将会获得如下图所示的.exe文件:…

cuda 各个版本下载

目录 cuda各个版本下载: cudnn会列出来对应cuda版本 需要登录,点击更多登录方式,微信登录也可以。 2022.08.10来看,网站还是有效的; cuda各个版本下载: https://developer.nvidia.com/cuda-toolkit-arc…

Cuda11.6 下载与安装[排坑版]

2022年7月份买了一台惠普光影7的笔记本,自带了Win11系统和Cuda 11.6。之前运行在Cuda10上的tensorflow代码在运行过程中调用cuda就会报错。cuda11.6是预装的,还很难替换。重装Win10系统还把电脑干崩了。无奈花钱重装成了原来的系统,下面讲一下…

Windows下载安装cuda10.1详细步骤

第一步:去百度上搜索cuda10.1 第二步:点击Windows,x86_64,10,exe(local),然后点击Download[2.4G],即可下载 第三步:安装过程 (1)双击: &#xff…

系统结构图:最大扇入、扇出数,深度和宽度的区别

1、最大扇入扇出数 最大扇出数,即模块直接调用的下层模块数目,可以看一个模块向下辐射出去的第一层的模块数。 同理,最大扇入数,即模块被直接调用的上层模块最大数目,可以看一个模块向上辐射出去的第一层的模块数。 2、…

GoLang使用Goroutine+Channel实现流水线处理,扇入扇出思想解决流水线上下游供需不平衡

码字不易,转载请附原链,搬砖繁忙回复不及时见谅,技术交流请加QQ群:909211071 目的 在一些业务逻辑场景中, 我们要针对同一批数据依次进行不同的处理,并且它们之间是有先后顺序的。比如我们制造一个手机要经…

mccabe java_面向过程及面向对象的扇入及扇出

面向过程的扇入及扇出: 扇入:是指直接调用该模块的上级模块的个数。即:called by个数 (在McCabe 里度量名称为Fan in) 扇出:该模块直接调用的下级模块的个数。即:calls to 个数(在McCabe 里度量名称为Fan out) 面向对象…

代码静态分析思维体操—扇入、扇出、圈复杂度

静态分析是一种检查代码的方法,无需执行程序。它提供了一种机制,可以审查代码结构、控制流和数据流,检测潜在的可移植性和可维护性问题,计算适当的软件质量测度。 其中控制流分析包括:扇入、扇出和圈复杂度。 常见流程图结构 一种常见的程序分析方法是通过生成程序的有向控…