关于内存管理

本文详细探讨了堆和栈的区别,包括管理方式、申请大小、碎片问题、分配方式及效率,并介绍了内存管理原则、自动释放池的作用以及强指针与弱指针的概念。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.堆和栈的区别 
管理方式: 
对于栈来讲,是由编译器自动管理,无需我们手工控制; 
对于堆来说,释放工作由程序员控制,容易产生memory leak(内存泄露)。

申请大小:
 
栈:在Windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。这句话的意思是栈顶的地址和栈的最大容量是系统 预先规定好的,在WINDOWS下,栈的大小是2M(也有的说是1M,总之是一个编译时就确定的常数),如果申请的空间超过栈的剩余空间时,将提示 overflow(溢出)。因此,能从栈获得的空间较小。 

堆:堆是向高地址扩展的数据结构,是不连续的内存区域。这是由于系统是用链表来存储的空闲内存地 址的,自然是不连续的,而链表的遍历方向是由低地址向高地址。堆的大小受限于计算机系统中有效的虚拟内存。由此可见,堆获得的空间比较灵活,也比较大。 

 
碎片问题: 
对于堆来讲,频繁的new/delete势必会造成内存空间的不连续,从而造成大量的碎片,使程序效率降低。 
对于栈来讲,则不会存在这个 问题,因为栈是先进后出的队列,他们是如此的一一对应,以至于永远都不可能有一个内存块从栈中间弹出

分配方式: 
堆都是动态分配的,没有静态分配的堆。 
栈有2种分配方式:静态分配和动态分配。静态分配是编译器完成的,比如局部变量的分配。动态分配由 alloca函数进行分配,但是栈的动态分配和堆是不同的,他的动态分配是由编译器进行释放,无需我们手工实现。

分配效率: 
栈是机器系统提供的数据结构,计算机会在底层对栈提供支持:分配专门的寄存器存放栈的地址,压栈出栈都有专门的指令执行,这就决定了栈的 效率比较高。 
堆则是C/C++函数库提供的,它的机制是很复杂的。

2.内存管理原则是什么? 
1. 谁创建,谁释放(类似于“谁污染,谁治理”)。如果你通过alloc、new或copy来创建一个对象,那么你必须调用release或autorelease。换句话说,不是你创建的,就不用你去释放。 
例如,你在一个函数中alloc生成了一个对象,且这个对象只在这个函数中被使用,那么你必须在这个函数中调用release或autorelease。如果你在一个class的某个方法中alloc一个成员对象,且没有调用autorelease,那么你需要在这个类的dealloc方法中调用release;如果调用了autorelease,那么在dealloc方法中什么都不需要做。 
2. 除了alloc、new或copy之外的方法创建的对象都被声明了autorelease。 
3. 谁retain,谁release。只要你调用了retain,无论这个对象是如何生成的,你都要调用release。有时候你的代码中明明没有retain,可是系统会在默认实现中加入retain。

3.谈一谈自动释放池

自动释放池(Autorelease pool)是OC的一种内存自动回收机制,可以将一些临时变量通过自动释放池来回收统一释放.自动释放池本事销毁的时候,池子里面所有的对象都会做一次release操作. 
自动释放池并不包含实际的对象本身,仅仅是对池释放的时候要释放的对象的引用,通过像当前的自动释放池发送一条autorelease消息,就可以将一个对象添加到其中,以便以后释放。 
Cocoa应用程序中的每个线程都会维护一个自己的NSAutoreleasePool对象的堆栈。当一个线程终止时,它会自动地释放所有与自身相关的自动释放池。在基于Application Kit的应用程序中,自动释放池会在程序的主线程中被自动创建和销毁,所以,您的代码通常无需处理它们。但是,如果您在Application Kit的主线程之外发起Cocoa调用,则您需要创建自己的自动释放池。如果您正在编写一个Foundation应用程序,或者如果您拆分了一个线程,则是属于这种情况。

4.什么是强指针,什么是弱指针? 
强指针在当进行对象地址的赋值操作时会保留新值并释放旧值,当其退出指针作用域时释放。而弱指针则类似于手动内存管理时的指针,它仅仅是一个指针而已,不做额外的保留和释放,在对象被回收时会自动置为nil。在程序中使用__strong关键字指定强指针,使用__weak关键字指定弱指针,默认为强指针。

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal;"><span style="font-size:24px;"><span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">if</span> (someCondition)
{
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSMutableArray</span>* stringArray = [<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSMutableArray</span> arrayWithObjects:@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"A"</span>, @<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"B"</span>, @<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"C"</span>, <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>];
    <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">id</span> firstObj = [stringArray objectAtIndex:<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>];
    [stringArray removeObjectAtIndex:<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>];
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%@"</span>, firstObj);
}</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">1</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">2</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">3</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">4</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">5</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">6</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">7</span></li></ul>

if语句块的第一行代码创建了一个数组,其中包含三个字符串对象,然后将其地址赋值给stringArray。第二行将数组的首元素地址赋值给firstObj,即指向字符串@”A”。第三行将数组首元素移出数组。第四行打印firstObj的描述信息。最后退出if语句块。

在未开启ARC时,由于元素在被移出数组时会被数组释放,所以在调用NSLog函数时@”A”已经被回收,firstObj指向的是一个无效的地址(野指针、僵尸),这会导致程序崩溃。

而当开启了ARC时,上述代码就是正确的。当执行到第二行时,stringArray和firstObj均为强指针(默认为强指针),分别保留数组和@”A”,此时@”A”的所有者为数组和firstObj。到第三行,@”A”的所有者为firstObj,未被回收,所以NSLog语句没有问题。最后退出if语句块时,stringArray和firstObj退出作用域,分别释放并回收数组和@”A”。

当然,你可以在定义stringArray和firstObj时指定__strong关键字:

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal;"><span style="font-size:24px;">[plain] view plaincopy
__<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">strong</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSMutableArray</span>* stringArray = [<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSMutableArray</span> arrayWithObjects:@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"A"</span>, @<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"B"</span>, @<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"C"</span>, <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>];
__<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">strong</span> <span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">id</span> firstObj = [stringArray objectAtIndex:<span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">0</span>];</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">1</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">2</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">3</span></li></ul>

不过由于默认为强指针,所以__strong关键字是可以忽略的。

由于强指针会一直保留着对象,所以当你确实需要将其释放时,需要手动将强指针赋值为nil,否则对象一直不会被回收,会导致系统内存资源不足。

再来说说弱指针。请看如下代码:

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal;"><span style="font-size:24px;">[plain] view plaincopy
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span>* strongPtr = [[<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span> alloc] initWithCString:<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"A"</span> encoding:NSUTF8StringEncoding];
__<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">weak</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSString</span>* weakPtr = strongPtr;
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%@"</span>, weakPtr);
strongPtr = <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>;
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%@"</span>, weakPtr);

<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">double</span> delayInSeconds = <span class="hljs-number" style="color: rgb(0, 102, 102); box-sizing: border-box;">3.0</span>;
dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, (int64_t)(delayInSeconds * NSEC_PER_SEC));
dispatch_after(popTime, dispatch_get_main_queue(), ^(<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">void</span>){
    <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"After 3 seconds: %@"</span>, weakPtr);
});</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">1</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">2</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">3</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">4</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">5</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">6</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">7</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">8</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">9</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">10</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">11</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">12</span></li></ul>

由于firstObj为弱指针,前两个NSLog输出为A,而最后一个则为(null)。这说明当对象被回收时,所有指向该对象的弱指针均会被置为nil,但这需要时间。

读到这里,你应该知道下面的代码有什么问题了吧:

<code class="hljs objectivec has-numbering" style="display: block; padding: 0px; background-color: transparent; color: inherit; box-sizing: border-box; font-family: 'Source Code Pro', monospace; white-space: pre; border-top-left-radius: 0px; border-top-right-radius: 0px; border-bottom-right-radius: 0px; border-bottom-left-radius: 0px; word-wrap: normal;"><span style="font-size:24px;">[plain] view plaincopy
__<span class="hljs-keyword" style="color: rgb(0, 0, 136); box-sizing: border-box;">weak</span> <span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSArray</span>* array = [[<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSArray</span> alloc] initWithObjects:@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"Puzhi Li"</span>, <span class="hljs-literal" style="color: rgb(0, 102, 102); box-sizing: border-box;">nil</span>];
<span class="hljs-built_in" style="color: rgb(102, 0, 102); box-sizing: border-box;">NSLog</span>(@<span class="hljs-string" style="color: rgb(0, 136, 0); box-sizing: border-box;">"%@"</span>, array);</span></code><ul class="pre-numbering" style="box-sizing: border-box; position: absolute; width: 50px; background-color: rgb(238, 238, 238); top: 0px; left: 0px; margin: 0px; padding: 6px 0px 40px; border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); list-style: none; text-align: right;"><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">1</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">2</span></li><li style="box-sizing: border-box; padding: 0px 5px;"><span style="font-size:24px;">3</span></li></ul>

当启用了ARC之后,(基本上)不会出现内存泄漏、使用野指针、访问僵尸对象的情况 。

5.多次调用对象的autorelease方法会导致什么问题? 
多次将地址存到自动释放池中,导致野指针异常

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值