iOS的内存管理

iOS的内存管理

今晚有空,总结一下学习iOS内存管理的一些认识。
文章中可能会涉及一些相对底层的知识,C的内存管理知识,慎入。

前方高能!
前方高能!!
前方高能!!!


内存结构

经典的内存划分:栈、堆、BSS段、数据段、代码段。如 下图 :
内存结构

先说堆栈:在C语言里,可以简单的说malloc等方法主动申请内存,其内存空间是在堆上,其他的在栈上。
展开一点说,临时变量是在栈上的,一般常用的指针也是存放在栈上的,而指针指向的内容在堆上。这么说可能不是很好理解。一个简单的例子:

int *foo = (int *)malloc(sizeof(int)*10);

这里我们申请了10个int长度(40byte)的内存空间,指向这40byte地址的指针foo存放在栈上,而这40byte内存地址是堆上的。

堆和栈的生长方向也是不一样的,栈是向下生长的,堆是向上生长的,二者是一个闭合的区间。如果你能记住上面的图,这个知识点就很容易记住了。如果栈向上生长,就溢出了→_→

内存管理手段也是不同的,堆是程序员手动管理内存,栈是系统自动管理(有木有MRC和ARC的即视感(≧▽≦)/)。

为什么要划分出堆和栈来呢,这个不是很好说的明白,你只要知道栈是一级缓存,堆是二级缓存,栈的读写速度快于堆就OK了。这里有一丢丢类似于内存和硬盘的区别(实际上不是)。

然后呢,就会有一个问题出现了,栈资源是比较紧俏了(参考手机运存),所以我们在涉及到栈操作时一定要千万小心。系统为每个程序分配的栈大小,具体有多少我不太记得了,有兴趣的同学可以查一下。只记得一般不超过10M。大家可以看下自己app的运行内存就知道这10M有多宝贵了。

关于堆栈溢出,我会单独列另一篇博客来讲,此处保持神秘感。
撒花,♪(^∇^*)。


引用计数

OC中说道内存管理,必然绕不开的就是引用计数技术,这也是很多面试官非常热衷的话题。那么什么是引用技术呢。

首先,要澄清一点,引用计数不是OC首创的,在远古的C时代,就有这么个东西了。而OC在面向对象编程上封装了该技术。

下面进入正题。狭义说的内存管理特制堆内存管理,就是我们常说的需要程序员手动管理的内存。在C语言中,对于一块已申请的内存,当我们不再需要使用时,需要手动的free掉这块内存。否则当函数的生命周期结束时,就会发生内存泄露。内存泄露怎么发生的呢。如下:

(void) foo
{
    int *bar = (int *)malloc(sizeof(int)*10);
}

前方高能:下面是一些冷门C知识,无感的同学请跳过。

程序对内存的使用是这样的,圈一块地(内存),声明使用权,这块地是我的(引用flag),别人不能动。当别人要用地时,发现这里有个flag,就不会去动他。当不需要使用这块地时,只需要把flag拔掉,就OK了。

根据函数内临时变量生命周期,我们知道,当函数运行结束时,会释放掉bar,这里释放的是存储bar指针的栈空间,这是系统自动执行的。这时一件恐怖的事情发生了,圈地的flag还插着,但是我们已经失去了对flag的控制权(flag所在的栈地址已被释放),之后任何人都无法动这块圈好的地了,包括我自己。此时就出现了内存泄露。

下面回到OC。引用技术的原理,就是对每块已经申请的内存设计一个计数器(初始为1),当有其他人想用我们申请的这块内存时,就让引用计数加1,当他不用的时候就减1。当引用计数器为1时,若又有人说我不用这块地址了,就说明再也没人需要用了,这块内存可以释放掉。
这里有一个小知识,引用计数为1时,再release不会减到0,而是会直接释放掉,这涉及到机器运行效率的问题。为了说话方便,我们一般会说减到0。

在C语言的内存管理上,有两种行为,一种是“谁申请谁释放”,就是说内存的释放必须由拥有者释放,其他引用都是纸老虎。一种是“谁拥有谁释放”,规定只要你引用了这块地址,都可以做释放操作。孰优孰劣难以道清,只能说OC其实是偏向于谁拥有谁释放的。


MRC

终于告别C了,进入OC内存管理,很多同学可以松一口气了。MRC,OC中的手动内存管理。相较于C的free,进行手动内存管理,MRC其实只是相当于面向对象的手动内存管理。MRC把对象的内存管理封装了一下,让你可以不去理对象内存的生命周期。但是总的来说还是很麻烦的。

用一下你就会发现,MRC和C手动内存管理都是走的同一个路子,不然为啥二者名字辣么像捏。

后来人们发现,大多数情况下,一个对象只会有一个持有者,不会有很多次引用。懒人推动了科技进步,于是有了自动释放池。

自动释放池又是个神马鬼?就是让你把对象丢到这个大池子里,当生命周期结束时,他会自动帮池子里的对象引用计数减1。注意,这里是减1,一个pool只能减1,而不是一个遍历,减到0;


ARC

有木有觉得MRC仍然反人类啊(我不会告诉你我连线程锁都自己封过→_→)。
对于这种反人类的东西,必须坚决干掉。世界是属于懒人的。所以有了ARC。

墙裂建议使用ARC,对于MRC项目也建议逐渐重构改为ARC。ARC基本做到了不需要太操心内存问题,程序简洁干练很多。需要说明的是,无论自动释放池还是ARC都是一种编译时行为,也就是说,是编译器手动加上了release,再手动改为free这样一个过程。而不是运行时动态调整的。

ARC下貌似没什么好讲的,但是程序员的双手解放了,总得在桌子下面做点啥吧。
ARC里面有一个很关键的概念就是强引用,若引用。

这是个什么概念呢,简单讲,就是强引用引用计数加1,弱引用引用计数不变。这会造成一个后果,就是弱引用对内存释放没有发言权。这种后果是良性的,在某些特殊场景下灰常好用。比如:

#import <Foundation/Foundation.h>
#import "Dog.h"

@interface Person : NSObject

@property (nonatomic, strong) Dog *dog;

@end
#import <Foundation/Foundation.h>
#import "Person.h"

@interface Dog : NSObject

@property (nonatomic, strong)Person *person;

@end

有一个Dog类,有一个主人person,有一个Person类有一个宠物dog。二者相互引用,就造成了一个死循环,导致二者无法正常释放内存,这里就用到了弱引用,将二者中任意一个属性设为weak,就可以解除该循环引用。


可变和不可变对象

在开发中,会经常遇到可变数组、不可变数组,可变字符串、不可变字符串。简单写一个:

char *str = "Hello World";
    NSLog(@"字符串常量\t---> %p", str);

    NSString *string = @"Hello World";
    NSLog(@"字符串\t\t---> %p",string);
    NSMutableString *mutString = [NSMutableString stringWithString:@"Hello World"];
    NSLog(@"可变字符串\t---> %p", mutString);

以及运行结果

字符串常量   ---> 0x10436765b
字符串      ---> 0x104368088
可变字符串   ---> 0x7f991159e2c0

这里为了清楚,将部分运行结果裁掉了。从结果上看可以看到,string和mutString明显不在同一个内存区域。有兴趣的童鞋可以看一下NSArray和NSMutableArray,有惊喜。

而这里string和str处在同一个内存区域,是系统的一个内存优化策略,有兴趣的童鞋可以搜索“字符串常量区”相关内容,此处不再科普。


深拷贝和浅拷贝

深拷贝和浅拷贝,分别对应着值传递和址传递。

这里比较容易引起争议的是copy和mutableCopy。

对于不可变对象,copy是浅拷贝,mutableCopy是深拷贝。
对于可变对象,二者都是深拷贝。

为毛会酱紫呢。改天单拉出来一章唠唠吧。平时只需要记住上面那句口诀就OK了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: iOS内存管理版本记录如下: 1. iOS 2.0及更早版本:使用手动管理内存的方式。 2. iOS 3.0:引入了基于引用计数的自动内存管理,使用retain和release函数来增加或减少对象的引用计数。 3. iOS 5.0:引入了ARC(自动引用计数)机制,ARC会在编译时自动插入retain和release代码,减少手动管理内存的工作。 4. iOS 7.0:引入了内存诊断工具Memory Usage Report,可以监测App内存使用情况,帮助开发者优化内存管理。 5. iOS 8.0:引入了一些新的API,如NSCache和NSURLSession,使得内存管理更加方便和灵活。 6. iOS 11.0:引入了基于图片大小的UIImage渲染机制,减少了内存占用。 7. iOS 13.0:引入了叫做“Scene”的多任务环境,使得内存管理更加复杂,需要更加小心谨慎地处理内存问题。 总的来说,随着iOS版本的不断更新,内存管理的机制也在不断地完善和优化,使得iOS应用能够更加高效地使用内存,提高用户体验。 ### 回答2: iOS内存管理是由操作系统自动管理的,在不同的版本中有所不同。 在iOS 5之前的版本中,内存管理主要依赖于手动管理引用计数(reference counting)来管理对象的生命周期。开发者需要手动调用retain和release方法来增加或减少对象的引用计数,以确保对象在不再需要时能够被正确释放。这种方式需要开发者非常谨慎地管理对象的引用,以避免内存泄漏或野指针等问题。 从iOS 5开始,iOS引入了自动引用计数(Automatic Reference Counting,ARC)的内存管理机制。ARC可以自动地插入retain、release和autorelease等方法的调用,使得开发者不再需要手动进行内存管理。开发者只需要关注对象的创建和使用,而不需要关心具体的内存管理细节。ARC减少了内存管理的工作量,提高了开发效率,并且减少了内存泄漏和野指针等问题的发生。不过,ARC并不是完全的自动化内存管理,开发者仍然需要遵循一些规则,比如避免循环引用等,以保证内存的正确释放。 随着iOS版本的不断更新,苹果不断改进和优化内存管理机制。每个新版本都带来了更好的性能和更高效的内存管理。开发者可以通过关注苹果的官方文档和开发者社区中的更新内容来了解每个版本中的具体变化和改进。 总结来说,iOS内存管理从手动的引用计数到自动引用计数的演变,极大地简化了开发者的工作,并提高了应用的性能和稳定性。随着不断的改进和优化,iOS内存管理会越来越高效和可靠。 ### 回答3: iOS内存管理版本记录是指苹果公司在不同版本的iOS操作系统中对于内存管理方面的改进和更新记录。随着iOS版本的不断迭代,苹果在内存管理方面进行了一系列的优化和改进,以提高系统的稳定性和性能。 首先,在早期的iOS版本中,苹果采用了手动内存管理的方式,即开发人员需要手动创建和释放内存,容易出现内存泄漏和内存溢出等问题。为了解决这些问题,苹果在iOS5版本中引入了自动引用计数(ARC)机制。ARC机制能够通过编译器自动生成内存管理代码,避免了手动管理内存带来的问题。 其次,iOS6版本引入了内存分页机制。这个机制能够将应用程序内存分成不同的页,将不常用的页置于闲置列表中,从而释放出更多的内存空间。这些闲置列表中的页能够在需要时快速恢复到内存中,减少了内存压力。 此外,iOS7版本中进一步提升了内存管理的能力。苹果在这个版本中引入了内存压缩技术,将内存中的数据进行压缩,从而提高了内存利用率。此外,iOS7还引入了资源清理功能,可以自动清理不再使用的资源,释放内存空间。 最后,在iOS13版本中,苹果进一步改进了内存管理策略。该版本中引入了后台内存优化功能,能够自动优化应用在后台运行时的内存占用,减少了后台应用对于系统内存的占用和影响。 综上所述,iOS内存管理版本记录反映了苹果在不同版本的iOS操作系统中对于内存管理方面的改进和优化。这些改进和优化使得iOS系统更加稳定和高效,并且提升了应用程序的性能和用户体验。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值