为了开学的面试,就在博客里总结一下面试会问到的问题,今天就来谈谈内存管理,看到一篇文章非常不错,http://vinceyuan.cnblogs.com/,深入浅出,推荐大家去看看!

  Objective-C使用一种(Retain Count)引用计数的机制来管理内存,在OC中,每个对象都持有自己的retain count,引用计数可以理解为就是一个计数器,当对象alloc创建的时候,会自动设置为1,当给对象发送retain消息的时候,引用计数会加1,当给对象发送release消息的时候,引用计数会减1,当引用计数为0的时候,对象会释放所占用的内存,这就是内存管理的机制,听起来比较容易吧,下面就进一步分析这种机制。

  首先,我们应该知道为什么要这样做?我们经常在不同的对象中引用相同的对象,例如:假设我们用不同电脑远程连接到同一台服务器进行远程操作,转化成OC语言就是不同电脑对象引用相同的服务器对象,这时候有一台电脑在服务器上敲了shutdown命令,让服务器挂掉了,这时候我们所有电脑都连接不上去了,其他人就工作不了了,引用计数其实就像一个计数开关,只有当没有电脑连接的时候,计数为0,才允许执行shutdown命令(非常理想状态下,现实中可不要这样做)。

  不同于java的GC回收机制,java中当没有对象引用指向原先分配给某个对象的内存时,该内存便成为垃圾。JVM的一个系统级线程会自动释放该内存块,除了释放没用的对象,垃圾回收也可以清除内存记录碎片。由于创建对象和垃圾回收器释放丢弃对象所占的内存空间,内存会出现碎片。碎片是分配给对象的内存块之间的空闲内存洞。碎片整理将所占用的堆内存移到堆的一端,JVM将整理出的内存分配给新的对象。OC中这种特有的retain count机制,给我们更多权限,让开发者去控制对象释放的时间以及如何去释放,所以我们得更加小心,过早的释放内存,可能会引起程序崩溃,长时间不释放占用的内存,程序在运行一段时间后可能会发生内存泄露。

  Objective-C采用了引用计数(retain count)。对象的内部保存一个数字,表示被引用的次数。例如,某个对象被两个指针所指向(引用)那么它的retain count为2。需要销毁对象的时候,不直接调用dealloc,而是调用release。release会让retain count减1,只有retain count等于0,系统才会调用dealloc真正销毁这个对象。

        ClassA *obj1 = [[ClassA alloc] init];//对象生成时,retain count = 1

        [obj1 release]; //release使retain count减1,retain count = 0,dealloc自动被调用,对象被销毁
        

 下面观看一个例子

ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj1 hello]; //输出hello
[obj1 release]; //retain count = 0,对象被销毁
[obj2 hello];
[obj2 release];

obj2引用了obj1,此时retain count为1,当obj1执行完消息释放后,retain count=0,此时obj2变成了无效指针,这里再执行[obj2 release]会引起内存的过度释放

所以一定要谨记,不是alloc创建,而是指针赋值的时候,一定要retain,拿到对象的所有权

ClassA *obj1 = [[ClassA alloc] init]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj2 retain]; //retain count = 2
[obj1 hello]; //输出hello
[obj1 release]; //retain count = 2 – 1 = 1
[obj2 hello]; //输出hello
[obj2 release]; //retain count = 0,对象被销毁

这样写的确可以解决问题,但是如果对象非常多得时候,这样的操作会不会太繁琐了点,有没有简单一点的解决办法?所以oc引入了自动释放池autorelease pool,这也不同于java的全自动垃圾回收

新生成的对象调用autorelease就可以了

ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1 但无需调用release

如果存在指针赋值,与上面的代码也相似

ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1
ClassA *obj2 = obj1; //retain count = 1
[obj2 retain]; //retain count = 2
[obj1 hello]; //输出hello
//对于obj1,无需调用(实际上不能调用)release
[obj2 hello]; //输出hello
[obj2 release]; //retain count = 2-1 = 1

这里有个有趣的问题,retain count不是1么,还不能销毁呀,什么时候才能销毁呢?

所以我们得了解一下autorelease pool的原理机制。

1)autorelease pool不是天生的,需要手动创立。只不过在新建一个iphone项目时,xcode会自动帮你写好。autorelease pool的真名是NSAutoreleasePool。

  

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

2)NSAutoreleasePool内部包含一个数组(NSMutableArray),用来保存声明为autorelease的所有对象。如果一个对象声明为autorelease,系统所做的工作就是把这个对象加入到这个数组中去。

ClassA *obj1 = [[[ClassA alloc] init] autorelease]; //retain count = 1,把此对象加入autorelease pool中

3)   NSAutoreleasePool自身在销毁的时候,会遍历一遍这个数组,release数组中的每个成员。如果此时数组中成员的retain count为1,那么release之后,retain count为0,对象正式被销毁。如果此时数组中成员的retain count大于1,那么release之后,retain count大于0,此对象依然没有被销毁,内存泄露。

那是不是有了自动释放池autorelease pool就万无一失了,其实不然

默认只有一个自动释放池

int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool;
pool = [[NSAutoreleasePool alloc] init]; // do something [pool release];
return ();
} // main

所有标记为autorelease的对象在这个pool内被销毁,但是如果这个自动释放池里面含有大量autorelease的对象,还是容易造成内存不足的情况,比如说:

int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i, j;
for (i = ; i < ; i++ )
{
for (j = ; j < ; j++ )
[NSString stringWithFormat:@""];//产生的对象是autorelease的。
}
[pool release];
return ();
} // main

这种情况,大量内存被占用,只有poll销毁的时候,那些声明为autorelease对象才被销毁,这对于ios程序来说并不乐观,iphone内存本身有限,那有没有更好的解决办法,所以我们可以用autorelease嵌套机制来控制。

Objective-C程序中可以嵌套创建多个autorelease pool。在需要大量创建局部变量的时候,可以创建内嵌的autorelease pool来及时释放内存。

int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i, j;
for (i = ; i < ; i++ )
{
NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
for (j = ; j < ; j++ )
[NSString stringWithFormat:@""];//产生的对象是autorelease的。
[loopPool release];
}
[pool release];
return ();
} // main

iOS面试_1.浅析内存管理的更多相关文章

  1. iOS技术面试02:内存管理

    怎么保证多人开发进行内存泄露的检查. 如何定位内存泄露? 1> 使用Analyze进行代码的静态分析(检测有无潜在的内存泄露) 2> 通过leak检查在程序运行过程中有无内存泄露 3> ...

  2. IOS 非ARC开发内存管理的几条规则

    关于ios内存管理.在开发过程中,内存管理很重要,我简单说明一下. 1.正确用法 UIView *v = [[UIView alloc] init]; //分配后引用计数为1 [self.view a ...

  3. iOS 下ARC的内存管理机制

    本文来源于我个人的ARC学习笔记,旨在通过简明扼要的方式总结出iOS开发中ARC(Automatic Reference Counting,自动引用计数)内存管理技术的要点,所以不会涉及全部细节.这篇 ...

  4. iOS性能优化之内存管理:Analyze、Leaks、Allocations的使用和案例代码

    最近接了个小任务,和公司的iOS小伙伴们分享下instruments的具体使用,于是有了这篇博客...性能优化是一个很大的话题,这里讨论的主要是内存泄露部分. 一. 一些相关概念 很多人应该比较了解这 ...

  5. IOS基础之 (十一) 内存管理 ARC

    一 内存管理 1. set 方法内存管理的相关参数 retain: release旧值,retain新值(值适用于OC对象) assign:直接赋值(set方法默认,适用于非OC对象类型,即基本数据类 ...

  6. iOS中引用计数内存管理机制分析

    在 iOS 中引用计数是内存的管理方式,虽然在 iOS5 版本中,已经支持了自动引用计数管理模式,但理解它的运行方式有助于我们了解程序的运行原理,有助于 debug 程序. 操作系统的内存管理分成堆和 ...

  7. IOS基础 Day-1手动内存管理

    辞职回家打算自学IOS开发,就在借个地方记录一下 Day-1      手动内存管理                   主要内容:release  retain必须配对好,不然会占用内存 慢慢积累导 ...

  8. iOS 非ARC基本内存管理系列 -手把手教你ARC——iOS/Mac开发ARC入门和使用(转)

    手把手教你ARC——iOS/Mac开发ARC入门和使用 Revolution of Objective-c 本文部分实例取自iOS 5 Toturail一书中关于ARC的教程和公开内容,仅用于技术交流 ...

  9. iOS 非ARC基本内存管理系列 2-多对象内存管理(3) 利用@property来自动管理内存

    iOS 基本内存管理-多对象内存管理(2)中可以看到涉及到对象的引用都要手动管理内存:每个对象都需要写如下代码 // 1.对要传入的"新车"对象car和目前Person类对象所拥有 ...

随机推荐

  1. 渗透测试中如何科学地使用V*P*N

    环境说明 Windows7 虚拟机,作为VPN网关,负责拨VPN.VPN可以直接OPENVPN,也可以使用ShadowSocks+SSTap. Kali 虚拟机, 渗透测试工作机 配置步骤 Windo ...

  2. python基础之函数(自定义函数)

    函数: 函数的定义: 初中数学函数定义:一般的,在一个变化过程中,如果有两个变量x和y,并且对于x的每一个确定的值,y都有唯一确定的值与其对应,那么我们就把x称为自变量,把y称为因变量,y是x的函数. ...

  3. bootstrap——集合

    1.<nav class="navbar navbar-inverse"> inverse,反色,原导航条为白色,加-inverse后改为黑色   2.data-tog ...

  4. algorithm ch2 Merge_sort

    这是用分治法来对序列进行排序,将较长的一个序列分解为n个比较短的序列,然后分别处理这n个较小的段序列,最后合并.使用递归的来实现. 具体实现的代码如下: void MergeSort(int *A, ...

  5. linux平台从源码安装git【转】

    转自:http://blog.csdn.net/lianshaohua/article/details/50571560 版权声明:本文为博主原创文章,未经博主允许不得转载. 如果是ubuntu等能自 ...

  6. WEB字体,多列布局和伸缩盒

    WEB字体 语法 @font-face{ font-family:""; src:url() format() ... } 兼容性写法 @font-face { font-fami ...

  7. Git-回滚操作

    git revert是用一次新的commit来回滚之前的commit,git reset是直接删除指定的commit git log 查询回滚版本唯一commit标识代码 git reset --ha ...

  8. MYSQL通过索引优化数据库的查询

    #转载请联系 索引是什么? 索引是一种特殊的文件(InnoDB数据表上的索引是表空间的一个组成部分),它们包含着对数据表里所有记录的位置信息. 更通俗的说,数据库索引好比是一本书前面的目录,能加快数据 ...

  9. matlab保存图片成eps格式不全,导致latex中图片显示不全的问题

    我们经常会遇到这样的问题.用将matlab生成的图保存EPS格式后,用GSVIEW打开后,可以看到图片显示不全.遇到这种情况是,我们可以使用dvipdf——dvips的方法来生成PDF,这样生成的pd ...

  10. hdu2665(主席树模板题)

    hdu2665 题意 求区间第 k 小. 分析 参考 这类题目做法挺多的,例如 划分树. 这里使用主席树再写一发,不得不说主席树相比而言要好写的多,比起普通线段树,主席树就是复用了线段树共有的信息. ...