9、内存管理初级

内存管理的⽅式

内存溢出

iOS给每个应⽤程序提供了⼀定的内存,⽤于程序的运⾏。
iPhone 3GS内存30M左右,
iPhone 5S 内存80M左右。
⼀旦超出内存上限,程序就会Crash。
程序中最占内存的就是图⽚、⾳频、视频等资源⽂件。
3.5⼨⾮Retina屏幕(320 * 480)放⼀张全屏图⽚,占⽤字节数320 * 480 * 4(⼀个像素占4个字节,存放RGBA),即:600k Bytes。
iPhone 3GS同时读取60张图⽚就会crash。
4⼨屏幕(320 * 568 ),实际像素640 * 1136,程序存放⼀张全屏图⽚,占⽤字节数640 * 1136 * 4,即2.77M Bytes。
iPhone 5S同时读取40张图⽚就会crash。

野指针异常

对象内存空间已经被系统回收,仍然使⽤指针操作这块内存。野指针异常是程序crash的主要原因。代码量越⼤的程序,越难找出出现野指针的位置

内存管理的⽅式

垃圾回收(gc)

程序员只需要开辟内存空间,不需要⽤代码显⽰地释
放,系统来判断哪些空间不再被使⽤,并回收这些内存空间,以便再次分配。整个回收的过程不需要写任何代码,由系统⾃动完成垃圾回收。
Java开发中⼀直使⽤的就是垃圾回收技术

MRC(Manual Reference Count)⼈⼯引⽤计数

内存的开辟和释放都由
程序代码进⾏控制。相对垃圾回收来说,对内存的控制更加灵活,可以在需要释放的时候及时释放,对程序员的要求较⾼,程序员要熟悉内存管理的机制。

ARC(Auto Reference Count)⾃动引⽤计数

iOS 5.0的编译器特性,它允许⽤户只开辟空间,不⽤去释放空间。它不是垃圾回收!它的本质还是MRC,只是编译器帮程序员默认加了释放的代码


引⽤计数机制,影响计数的各个⽅法

C语⾔中,使⽤malloc和free,进⾏堆内存的创建和释放。堆内存只有正在使⽤和销毁两种状态。

实际开发中,可能会遇到,两个以上的指针使⽤同⼀块内存。C语⾔⽆法记录内存使⽤者的个数。

OC采⽤引⽤计数机制管理内存,当⼀个新的引⽤指向对象时,引⽤计数器就递增,当去掉⼀个引⽤时,引⽤计数就递减。当引⽤计数到零时,该对象就将释放占有的资源。

影响引⽤计数的⽅法

+alloc

开辟内存空间,让被开辟的内存空间的引⽤计数变为1。
这是由0到1的过程

-retain

引⽤计数加1,如果内存空间之前引⽤计数为1,ratain之后变为2,如果引⽤计数是5,retain之后变为6

-copy

把某⼀内存区域的内容拷⻉⼀份,拷⻉到新的内存空间⾥去,被拷⻉区域的引⽤计数不变,新的内存区域的引⽤计数为1

-release

引⽤计数减1,如果内存空间之前引⽤计数为4,release之后变为3,如果之前引⽤计数为1,release之后变为0,内存被系统回收。

-autorelease

未来的某⼀时刻引⽤计数减1。如果内存之前引⽤计
数为4,autorelease之后仍然为4,未来某个时刻会变为3

autoreleasepool

通过autoreleasepool控制autorelease对象的释放。
向⼀个对象发送autorelease消息,这个对象何时释放,取决于autoreleasepool。

NSAutoreleasePool *pool= [[NSAutoreleasePool alloc]init];
Person *p = [[Person alloc]init];//retainCount为1
[p retain];//retainCount为2
[p autorelease];//retainCount为2 未来的某个时刻释放
[pool release];//此时autorelease的对象引⽤计数-1
NSLog(@”%d”,[p retainCount]);//打印结果为1

NSAutoreleasePool *pool= [[NSAutoreleasePool alloc]init];和[pool release];就像⼀对括号,[xxx autorelease];必须写在两者之间。
[xxx autorelease];出现在了两者之间,pool就会把接收autorelease的对象给保存起来(以栈的⽅式,把对象压⼊栈)
当[pool release];的时候,pool会向之前保存的对象逐⼀发送release消息(对象出栈,越晚autorelease的对象,越早接收release消息)。

在iOS5之后,不再推荐使⽤NSAutoreleasePool类,使⽤@autoreleasepool{}替代。
之前写在NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];和[pool release];之间的代码,需要写在@autoreleasepool{}的⼤括号⾥。
出了⼤括号,⾃动释放池才向各个对象发送release消息。


dealloc⽅法

-dealloc是继承⾃⽗类的⽅法,当对象引⽤计数为0的时候,由对象⾃动调⽤

- (void)dealloc{
    //验证对象引⽤计数是否降为0
     NSLog(@“%@被销毁了”,self);
     [super dealloc];
}

内存管理的基本原则

  • 引⽤计数的增加和减少相等,当引⽤计数降为0之后,不应该再使⽤这块内存空间
  • 凡是使⽤了alloc、retain或者copy让内存的引⽤计数增加了,就需要使⽤release或者autorelease让内存的引⽤计数减少。在⼀段代码内,增加和减少的次数要相等

内存管理的原则
1)原则
只要还有人在使用某个对象,那么这个对象就不会被回收; 只要你想使用这个对象,那么就应该让这个对象的引用计数器+1; 当你不想使用这个对象时,应该让对象的引用计数器-1;
2)谁创建,谁release
(1)如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者 autorelease方法
(2)不是你创建的就不用你去负责
3)谁retain,谁release
只要你调用了retain,无论这个对象时如何生成的,你都要调用release
4)总结
有始有终,有加就应该有减。曾经让某个对象计数器加1,就应该让其在最后-1.


掌握copy的实现

跟retain不同,⼀个对象想要copy,⽣成⾃⼰的副本,需要实现NSCopying协议,定义copy的细节(如何copy)。如果类没有接受NSCopying协议⽽给对象发送copy消息,会引起crash

Person.h⽂件

@interface Person : NSObject<NSCopying>//实现NSCopying协议
@property(nonatomic, retain)NSString *name;
@property(nonatomic, assign)int age;
@end

Person.m⽂件

@implementation Person
//NSCopying协议必须实现的方法
-(id)copyWithZone:(NSZone *)zone{
    //用[[Person alloc]init]也可以,建议使用以下,好看噻~
    Person *p = [[Person allocWithZone:zone]init];

    //成员变量的指针只是一个赋值作用,指向的是同一块内存,是浅拷贝,地址相同
    /**/
    p.name = self.name;
    p.sex = self.sex;
    p.age = self.age;
    NSLog(@"%p,%p",p.name,self.name);


    //深拷贝,重新分配内存,将原来内存中的内容放到新的内存中,地址不同
    /*
    p.name = [[NSString alloc]initWithFormat:@"%@",self.name];
    NSLog(@"%p,%p",p.name,self.name);
    */

    return p;
}
@end

main.m文件

Person *p = [[Person alloc]init];
p.name = @”张三”;
p.age = 20;
Person *p2 = [p copy];//p2是p的副本,属于浅拷贝
//p2.name与p.name⼀样。p2.age与p.age⼀样。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值