iOS内存管理

本文深入探讨iOS中的内存管理,包括栈空间与堆空间的区别、内存管理的目标、MRR(手动引用计数)与ARC(自动引用计数)两种模型的特点及应用。特别介绍了MRR模型下的内存管理基本原则、如何避免内存泄漏和内存损坏等问题。

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

前提
(1). 以下是针对cocoa对象,不包括core foundation
(2). cocoa对象都是用引用计数来跟踪对象的内存使用情况的。
(3). 在子类里面父类先初始化和后释放的原则。自己想下为什么

栈空间和堆空间的区别。
我们说的内存管理都是基于堆空间的,因为函数内的栈空间是由编译器自己控制的。 
关于core foundation的内存管理,在下下一次介绍。

内存管理主要是解决以下两个问题:
(1). 内存泄漏,内存泄漏就是不再使用的对象内存没有及时释放,导致程序内存使用过量,可能影响程序的效率,严重过量时可能导致程序被ios系统终止。
(2). 释放或者重写正在使用的数据。引发内存损坏,会让程序崩溃或者损坏

ios内存管理模型分为MRR( manual retain-release ) 和 ARC( Automatic Reference Counting ) 
MRR (手动retain和release)
你需要自己控制你拥有的对象的内存的分配和释放。
内存管理基本原则
(1).  你拥有你创建的任何对象(以下名称开头的方法,alloc,copy,retain,mutableCopy,new等)
(2).  你拥有你retain的对象
(3).  你必须释放你拥有的不再需要使用的对象(release)
(4).  你不能释放不是你拥有的对象。 
exp.

- ( NSString  *)fullNameOne
{
    
NSString  *string = [[[ NSString   alloc initWithFormat : @"%@" @"123" autorelease ];
    
return  string;
}
- ( NSString  *)fullNameTwo
{
    
NSString  *string = [ NSString   stringWithFormat : @"%@ %@" , @"123" @"hello" ];
    
return  string;
}
- (
NSString  *)fullNameThree
{
    
NSString  *string = [[ NSString   alloc initWithFormat : @"%@" @"123" ];
    
return  string;
}

以上第一种和第二种没有问题,第三种存在内存泄漏的可能,因为调用fullName的人并不知道返回的string使用完了还自己手动需要释放。
为什么第二种方式没有alloc,调用alloc和没有调用alloc有什么区别?读者仔细想下。

使用accessor方法来简化内存管理
@interface  Memory :  NSObject
@property  ( nonatomic strong NSString  *username;
@property  ( nonatomic weak IBOutlet UILabel  *usernameDisplayArea;
@end

#import  “Memory.h"
@implementation  Memory
@synthesize  username =  _username ;
@synthesize   usernameDisplayArea  _ usernameDisplayArea ;
@end ;

以上代码有几个地方需要,1. strong和weak 2.  @synthesize  username =  _username ;的含义。username是属性名,而_username是你成员变量的名字。使用时,self.username实际是调用你的属性username的getter方法,而_username仅仅就是在使用你的成员变量。
tip:
不要在init和dealloc种使用accessor方法,原因:因为在init时候,你的对象还没有构造完成,所以不能使用当前没有构造完成的对象,会有不确定的时候发生。
弱引用用得最普遍的是IBOutlet,所有的IBOutlet都是交给了storyboard和xib文件进行管理的。 


在MRR中,如何手写setter方法?

- (void)setUsername:(NSString *)newUsername

{

    NSLog(@"when call setter!");

    [newUsername retain]; //这个地方暗藏玄机,retain必须要在usernamerelease之前,因为newUsername可能和username指向的同一个地方。

    [usernamerelease];

    username = newUsername;//指针的赋值,让他们两个指向同一个地方。

    //self.username = [myUsername retain]; //why here can not use self.username,这里会抛错误。因为使用self.username = xxx时候就是使用了setter

}

有两个点需要注意:
第一,如果属性是bool类型,那么getter方法的名字应该是isXXX,其他情况都是XXX
第二,setter方法,注意看上面这个setter方法。为什么不需要判断username是不是空呢?因为在oc中,发送消息给nil是没有问题的,不会发生任何事情。

以下代码有个小tip:
- ( NSString  *)getCurrentUsername:(Person *)person
{
    [person 
retain ];  //why need retain here …. 作用,当 person 传进来的时候, person 没有被释放,但是当我们正在使用这个对象的时候,这个 person 对象可能在外面被释放了,我们的程序会因此崩溃。因此为了防止意外释放,我们要先retain这个对象,确保我们使用的过程中不会出错。
    
NSString  *username = [NSString stringFormatWith: @"%@ living in room 32." person.name];
    [person 
release ];
    
return  username;
}
tips:
如果发送消息给一个已经被释放的对象,程序会崩溃。
避免释放你正在使用的对象,需要注意以下两个情况
1. 当一个对象从一个基础的集合中释放时
2. 当对象的父对象被释放时 

autorelease 是延迟发送release消息给你的对象。最近一层的autorelease pool释放时,对应的release消息被发送。
autorelease pool 的问题,在arc中已经被@autoreleasepool{} blocks取代了。
autorelease 解决了以下几个问题: 
autorelease pool降低了应用程序的peak memory footprint,需要使用autorelease pool来管理产生的线程,因为线程保持自己的栈空间。

为避免过高的footprint,我们可以在分配适当量的临时变量后,drain掉pool。
- ( void )autoreleasePoolTest
{
    
NSAutoreleasePool  *pool = [[ NSAutoreleasePool   alloc init ];
    
for  ( int  i =  0 ; i <  1000000 ; i++) {
        
NSString  *s = [[[ NSString   alloc initWithFormat : @"%@" @"poolTest" autorelease ];
        
NSLog ( @"%@" , s);  //using some temp obj.
        
if  (i %  100  ==  1 ) {   //you can decide the number.
            [pool 
drain ];
            pool = [[
NSAutoreleasePool   alloc init ];
        }
    }
    [pool 
drain ];
}


ARC 自动引用计数
取消了显示的retain和release,autorelease等,编译器在编译阶段自动插入了retain和release这些语句。
ARC的主要目的是希望开发人员投入更多的精力去关注自己要实现的功能,把你从内存管理的泥潭中解放出来。 

ARC中引入了弱引用(weak)。弱引用在对象还拥有强引用前能使用引用的对象。
弱引用主要是为了解决retain cycle 问题。循环引用的问题,对象之间相互拥有。
Cocoa建立了一个约定,父对象必须保持对子对象的强引用,子对象必须保持对父对象的弱引用

写得比较乱,因为是按照apple文档,边写边思考写的。
下一篇blog专门介绍ARC.


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值