黑马程序员——OC基础——内存管理(一)

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

一,为什么要进行内存管理

1,由于移动设备的内存有限,所以每个APP所占的内存也是有限制的,当APP所占用的内存较多时,系统就会发出警告,这时就需要回收一些不需要继续使用的内存空间,比如回收一些不再使用的对象和变量等。

任何继承NSObject的对象,对其他的基本数据类型无效

本质原因是因为对象和其他数据类型在内存中的存储空间不一样,其他局部变量主要放于栈中,而对象存储在堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄露。

2,内存管理的黄金法则:

苹果官方文档原话:The basic rule to apple is everything thatincreases the reference counter with alloc,[mutable]copy[WithZone:] or retainis in charge of the corresponding [auto]release.

即:如果一个对象使用了alloc,[multable]copy,retain,那么你必须使用相应的release或者autonrelease

3,内存管理类型分类

基本类型和C语言的类型:

int,short,char,struct,enum,union等类型

OC类型:任何继承于NSObject对象都属于OC的类型

内存管理实际上是对OC类型的内存管理,它对基本数据类型和C语言的类型不管用

4,OC对象在内存中的结构

所有的OC类型的对象结构中都包含一个retainCount的引用计数

每一个OC对象都有一个4个字节的retainCount的计数器,表示当前对象被引用的计数。如果计数器为0,那么就释放这个对象。

规则:

1>OC类中实现了引用计数器,对象知道自己当前被引用的次数

2>对象创建时计数器为1.

3>如果需要引用对象,可以给对象发送一个retain消息,对象计数器+1

4>使用release可以使对象计数器-1.

5>当计数器为0时,自动调用对象的dealloc函数,对象就会释放内存

6>计数器为0的对象不能再使用release和其他方法

5,举例说明:

比如有一个引擎类Engine,有一个汽车类Car,Car里面有一个Engine的实例变量,一个setter和getter方法,如下

#import "Car.h"
@implementation Car

-(void)setEngine:(Engine *)engine
{
    _engine = engine;
}
- (Engine *)engine
{
    return _engine;
}
- (void)dealloc
{
    NSLog(@"Car is dealloc");
    [super dealloc];
}
@end
在main方法里:
Engine *engine1 = [[Engine alloc]init];
[engine1 setID:1];
//在创建一个汽车,设置汽车的引擎
Car *car = [[Car alloc]init]
[car setEngine:engine1];
分析:现在有两个引用指向这个Engine对象,engine1和Car中的_engine,可是这个Engine对象的引用计数还未1,因为set方法中并没有使用retain。那么不管是那个引用调用release,那么林外一个都会引用都会指向一块释放掉的内存,那么肯定会发生错误。

setter方法改进:

- (void)setEngine:(Engine *)engine
{
    _engine = [engine retain];
}
再在main中使用:
//先创建一个引擎  
 Engine* engine1=[[Engine alloc]init];  
 [engine1 setID:1];  
 //在创建一个汽车,设置汽车的引擎  
 Car* car=[[Car alloc]init];//retainCount=1  
 [car setEngine:engine1];//retainCount=2,因为使用了retain,所以retainCount=2,  
  
 //假设还有一个引擎  
 Engine* engine2=[[Engine alloc]init];  
 [engine2 setID:2];  
   
 //这个汽车要换一个引擎,自然又要调用settr方法         
  [car setEngine:engine2];  
    
分析:在这里,汽车换了一个引擎,那么它的_engine就不在指向engine1的哪个对象的内存了,而是换成了engine2,也就是说engine1的哪个对象指向的内存的引用只有一个  可是它的retainCount是两个,这就是问题的所在了。所以仍然需要改进

再改进:

-(void)setEngine:(Engine*) engine  
{  
     [_engine release];//在设置之前,先release,那么在设置的时候,就会自动将前面的一个引用release掉  
    _engine=[engine retain];//多了一个引用,retainCount+1  
}
//先创建一个引擎  
Engine* engine1=[[Engine alloc]init];  
[engine1 setID:1];  
//在创建一个汽车,设置汽车的引擎  
Car* car=[[Car alloc]init];//retainCount=1  
[car setEngine:engine1];//retainCount=2,因为使用了retain,所以retainCount=2,  
  
//如果进行了一个误操作,又设置了一次engine1       
 [car setEngine:engine1];  

分析:那么,又要重新调用一次setter方法,这根本就是无意义的操作,浪费资源,所以要在设置之间加上判断

改进:

-(void)setEngine:(Engine*) engine  
{  
   if(_engine!=engine){//判断是否重复设置  
           [_engine release];//在设置之前,先release,那么在设置的时候,就会自动将前面的一个引用release掉  
           _engine=[engine retain];//多了一个引用,retainCount+1  
      }  
 }  

现在setter方法基本没有问题了,那么在当我们要释放掉一个car对象的时候,必须也要释放它里面的_engine的引用,所以,要重写car的dealloc方法。
-(void)dealloc  
{  
    [_engine release]; //在释放car的时候,释放掉它对engine的引用  
    [super dealloc];  
}  
所以,setter方法中应该是:
-(void)setEngine:(Engine*) engine  
{  
   if(_engine!=engine){//判断是否重复设置  
           [_engine release];//在设置之前,先release,那么在设置的时候,就会自动将前面的一个引用release掉  
           _engine=[engine retain];//多了一个引用,retainCount+1  
      }  
}
dealloc的写法是:
-(void)dealloc  
{  
    [_engine setEngine:nil]; //在释放car的时候,对setEngine设置为nil,它不仅会release掉,并且指向nil,即使误操作调用也不会出错。  
    [super dealloc];  
}

property中的setter语法关键字

在property属性中有三个关键字定义关于展开setter方法中的语法,assgin(缺省),retain,copy。当然这三个关键字是互斥的。

1、assgin展开stter的写法

-(void)setEngine:(Engine*) engine  
{  
     _engine=engine;  
} 

2、retain展开的写法
-(void)setEngine:(Engine*) engine  
{  
   if(_engine!=engine){//判断是否重复设置  
           [_engine release];//在设置之前,先release,那么在设置的时候,就会自动将前面的一个引用release掉  
           _engine=[engine retain];//多了一个引用,retainCount+1  
      }  
 } 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值