内存管理

1.为什么需要管理内存
移动设备的内存是有限的,每一个app所能占用的内存是有限的,如果不进行内存管理,那么app就会出现闪退,崩溃等情况。
2.什么是内存管理
内存管理是指软件运行时,对内存资源的分配和使用技术,其最主要的目的是如何高效,快速的分配内存,并且在适当的时候释放和回收内存资源。
3.如何进行内存管理
iOS开发中数据一般是存储在堆和栈中的,然后栈内存会自动回收,并不需要我们进行手动管理,因此,我们要管理的就是存放在堆内存中的数据。

那么,堆和栈都是用来存储什么数据的呢?

内存中有五大区域:栈,堆,BSS段,数据段,代码段,这五大区域的作用分别是:

栈:用来存储局部变量
堆:用来存储对象,允许程序员手动申请
BSS段:未初始化的全局变量,静态变量
数据段:初始化的全局变量,静态变量和常量
代码段:代码

因此,一般情况下,我们只需要关注对象什么时候创建,什么时候释放即可。

4.引用计数器
(1)什么是引用计数器?
每个OC对象都有自己的引用计数器,它是一个整数
可以理解为有多少个人正在使用或者被引用了多少次
(2)系统是如何判断什么时候需要回收一个对象的?
根据对象的引用计数器属。
当对象的引用计数器为0时,这个对象就会被释放,只要这个对象的引用计数器不为0,就不会被释放。
当创建(alloc,new,copy)一个对象的实例,并在堆内存上申请内存时,对象的引用计数器就为1。在其它对象中需要使用这个对象时,这个对象的引用计数器就会+1,需要释放一个对象时,就将这个对象的引用计数器-1,直到对象的引用计数为0,这个对象就会被自动释放
(3)如何操作一个对象的引用计数器?
给对象发送一个retain消息,可以使引用计数器值+1
给对象发送一个release消息,可以使引用计数器-1
给对象发送一个retainCount消息,可以方便程序员查看引用计数器的值,但是这个值并不都是正确的。

注意:给对象发送release消息,并不代表这个对象就一定会释放,只有当对象的引用计数器为0时,这个对象才会释放。

(4)dealloc方法
dealloc是一个对象方法,dealloc方法和load,initialization是一样的,都不能手动调用,都是系统自动调用的。

5.单个对象的内存管理

int main(int argc, const char * argv[]){
    @autoreleasepool{
        Person *p = [[Person alloc] init];    //1
        //指针p是一个局部变量,所以p存储在栈中,Person对象存储在堆内存中,所以,当大括号结束的时候,p就会自动释放了,但是因为Person对象在堆内存中,并不会自动释放,所以,需要手动释放。
        [p release];    //如果直接这样写,会报错,因为是在ARC模式下,需要把程序切换到MRC模式下才不会报错。
        
    }
}

@implementation Person
{

    -(void)dealloc{
        NSLog(@"%s", __func__);
    }
    [super dealloc];
}

注意:
(1)ARC:Automatic Reference Counting(自动引用计数)
不需要程序员管理,编译器xcode会在适当的地方自动加上release/retain等代码
MRC:Matual Reference Counting(手动引用计数)
需要手动添加release/retain等代码
(2)将项目改成MRC模式的:项目->搜索Automatic Reference Counting改为NO

内存管理的原则:一次alloc对应一次release,一次retain对应一次release,一次copy对应一次release,一次new对应一次release

6.多个对象的内存管理

存在依赖关系的对象之间的内存管理

比如:

@interface Room
@property int no;
@end
@implementation Room

-(void)dealloc{
   NSLog(@"%s",__func__);
}
@end

@class Room;
@interface Person : NSObject
{
    Room *_room;
}
-(void)setRoom:(Room *)room;
-(Room *)room;
@end
@implementation Person
-(void)setRoom:(Room *)room{
   _room = room;
}
-(Room *)room{
   return _room;
}
-(void)dealloc{
   NSLog(@"%s",__func__);
}
@end

int main(int argc, const char *argv[]){
   Person *p = [[Person alloc] init];
   Room *r = [[Room alloc] init];
   r.no = 888;
   p._room = r;
   [r release];
   [p release];

   
}
//如果只是这样写的话,

图1
当程序执行到第3行的时候,数据在内存中的存储形式如图1所示。

当执行[r release]的时候,Room对象的retainCount减1,这时Room对象就会被释放。
但是,此时Person对象还没被释放,Person对象还没有被释放时,Room对象是不能被释放的。因此,这样写是不对的。

因此,对上面的代码进行修改

@interface Room
@property int no;
@end
@implementation Room

-(void)dealloc{
   NSLog(@"%s",__func__);
}
@end

@class Room;
@interface Person : NSObject
{
    Room *_room;
}
-(void)setRoom:(Room *)room;
-(Room *)room;
@end
@implementation Person
-(void)setRoom:(Room *)room{
   [room retain];   //当person对象使用room对象时,要对room对象的引用计数器加1
   _room = room;
}
-(Room *)room{
   return _room;
}
-(void)dealloc{
   NSLog(@"%s",__func__);
   [_room release];  //同时Person对象释放的时候,room对象也要释放
}
@end

int main(int argc, const char *argv[]){
   Person *p = [[Person alloc] init];
   Room *r = [[Room alloc] init];
   r.no = 888;
   p._room = r;
   [r release];
   [p release];

   
}

这样修改之后,再执行上面的代码时,room对象的引用计数器会变成2。再执行[r release]和[p release]时,就不会存在person对象还存在的情况下,room对象已经销毁的情况了,同时,Person对象销毁的同时,room对象也要销毁,这样才不会造成内存泄露。

接下来我们再看:

@interface Room
@property int no;
@end
@implementation Room

-(void)dealloc{
   NSLog(@"%s",__func__);
}
@end

@class Room;
@interface Person : NSObject
{
    Room *_room;
}
-(void)setRoom:(Room *)room;
-(Room *)room;
@end
@implementation Person
-(void)setRoom:(Room *)room{
   [room retain];   //当person对象使用room对象时,要对room对象的引用计数器加1
   _room = room;
}
-(Room *)room{
   return _room;
}
-(void)dealloc{
   NSLog(@"%s",__func__);
   [_room release];  //同时Person对象释放的时候,room对象也要释放
}
@end

int main(int argc, const char *argv[]){
   Person *p = [[Person alloc] init];
   Room *r = [[Room alloc] init];
   Room *r1 = [[Room alloc] init];
   r.no = 888;
   p._room = r;
   p._room = r1;
   [r release];
   [p release];

   
}

图2
如图2所示,换房的过程会造成内存泄露

因此在更换取值之前,应该将之前的房间释放掉

@interface Room
@property int no;
@end
@implementation Room

-(void)dealloc{
   NSLog(@"%s",__func__);
}
@end

@class Room;
@interface Person : NSObject
{
    Room *_room;
}
-(void)setRoom:(Room *)room;
-(Room *)room;
@end
@implementation Person
-(void)setRoom:(Room *)room{
   if(_room != room){
         [_room release];   //需要将之前的取值释放掉
   }
   [room retain];   //当person对象使用room对象时,要对room对象的引用计数器加1
   _room = room;
}
-(Room *)room{
   return _room;
}
-(void)dealloc{
   NSLog(@"%s",__func__);
   [_room release];  //同时Person对象释放的时候,room对象也要释放
}
@end

int main(int argc, const char *argv[]){
   Person *p = [[Person alloc] init];
   Room *r = [[Room alloc] init];
   Room *r1 = [[Room alloc] init];
   r.no = 888;
   p._room = r;
   p._room = r1;
   [r release];
   [p release];   
}

因此,setter方法最终的结果就是这样的了。如果对象的属性是一个对象,那就需要这样写setter和dealloc方法。

7.property的修饰符

retain:会自动帮我们生成setter/getter方法内存管理的代码
assign:不会自动帮我们生成setter/getter方法内存管理的代码

8.autorelease
autorelease是一种支持引用计数的内存管理方式,只要给对象发送一条autorelease消息,会将对象放到一个自动释放池中,当自动释放池被销毁时,会对池子里的所有对象发送一条release消息

@autoreleasepool {
 
        Person *p = [[Person alloc] init];
 
        [p autorelease];  
 
        //model can dongSomething you want
 
        NSLog(@"自动释放:end");
    } //自动释放池销毁
    //当执行到[p autorelease]时,对象会被加入到自动释放池中,当程序结束时,自动释放池会销毁,同时给池中的所有对象发送一个release消息

其它写法

//创建一个自动释放池
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
Person *p = [[[Person alloc] init] autorelease];
[pool release];

使用优点:
再也不用考虑什么时候写release
其实是延迟了对象的release

在一个程序中可以使用多个自动释放池,多个自动释放池也可以嵌套使用

@autoreleasepool{
   @autoreleasepool{
      @autoreleasepool{
           Person *p = [[Person alloc] init];
           [p run];
      }
   }
}

多个自动释放池是以栈的形式存储的。遵循先进后出的原则。
给一个对象发送一条autorelease消息,那么会直接把对象放到栈顶的自动释放池。

注意:
(1) [p autorelease]会返回对象本身。
(2) 自动释放池被销毁时,只是会给池子中的所有对象发送一个release消息,并不是销毁对象
(3) autorelease并不会修改对象的引用计数器

(4) autoreleasepool自动释放池可以创建多个
(5) 一定要在自动释放池中调用autorelease,才会将对象放入自动释放池中
(6)在自动释放池中创建了对象一定要调用autorelease,才会将对象放入自动释放池中
(7)如果创建的对象只使用1次,以后就不用了,则不建议在autoreleasepool中使用
(8)尽量不要在自动释放池中使用循环次数较多的对象
(9)千万不要过度释放,一个alloc对应一个autorelease或者一个release。
(10)Person *p = [[[Person alloc] init] autorelease] 可以一创建就使用autorelease
(11)也可以使用类工厂方法,将autorelease封装到类工厂方法中,MRC模式下Foundation库中的类工厂方法都封装了autorelease

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值