iOS面试_1.浅析内存管理

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

 

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

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

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

 

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

        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]会引发内存的过分释放iphone

因此必定要谨记,不是alloc建立,而是指针赋值的时候,必定要retain,拿到对象的全部权oop

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的全自动垃圾回收spa

新生成的对象调用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 count1,那么release以后,retain count0,对象正式被销毁。若是此时数组中成员的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 (0);
} // main

全部标记为autorelease的对象在这个pool内被销毁,可是若是这个自动释放池里面含有大量autorelease的对象,仍是容易形成内存不足的状况,好比说:

int main (int argc, const char *argv[])
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
int i, j;
for (i = 0; i < 100; i++ )
{
 for (j = 0; j < 100000; j++ )
    [NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。
}
[pool release];
return (0);
} // 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 = 0; i < 100; i++ )
{
 NSAutoreleasePool *loopPool = [[NSAutoreleasePool alloc] init];
 for (j = 0; j < 100000; j++ )
    [NSString stringWithFormat:@"1234567890"];//产生的对象是autorelease的。
 [loopPool release];
}
[pool release];
return (0);
} // main
相关文章
相关标签/搜索