⼀、内存管理的方式
1)简述
1>ios应用程序出现Crash(闪退),90%以上的原因是内存问题,了解内存常见问题,帮助我们减少出错几率。内存问题体现在两个方面:内存溢出,野指针异常
2>野指针异常:对象内存空间已经被系统回收,仍然使用指针操作这块内存。野指针异常是程序crash的主要原因。代码量越大的程序,越难找出现野指针的位置。
野指针->最好安全释放
3>垃圾回收 (gc)
程序员只需要开辟内存空间,不需要⽤代码显⽰地释放,系统来判断哪些空间不再被使用,并回收这些内存空间,以便再次分配。整个回收的过程不需要写任何代码,由系统自动完成垃圾回收。Java开发中一直使用的就是垃圾回收技术
人工引⽤计数:内存的开辟和释放都由程序代码进行控制。相对垃圾回收来说,对内存的控制更加灵活,可以在需要释放的时候及时释放,对程序员的要求较高,程序员要熟悉内存管理的机制。
ARC (Auto Reference Count)
自动引用计数:iOS 5.0的编译器特性,它允许用户只开辟空间,不用去释放空间。它不是垃圾回收!它的本质 还是MRC,只是编译器帮程序员默认加了释放的代码。
⼆、引用计数机制,影响计数的各个方法。
1)简述
C语⾔中,使⽤malloc和free,进⾏堆内存的创建和释放。堆内存只有正在使⽤和销毁两种状态。
OC采用引⽤计数机制管理内存,当一个新的引⽤指向对象时,引⽤计数器就递增,当去掉⼀个引⽤时,引⽤计数就递减。当引用计数到零时,该对象就将释放占有的资源。
+alloc :开辟内存空间,让被开辟的内存空间的引用计数变为1。 这是由0到1的过程。
-retain :引用计数加1,如果内存空间之前引用计数为1,ratain之后变为2
-release :引⽤计数减1,如果内存空间之前引用计数为4,release之后变为3
-copy :把某一内存区域的内容拷贝一份,拷⻉到新的内存空间⾥去,被拷⻉区域的引用计数不变,新的内存区域的引⽤计数为1。
-autorelease:未来的某一时刻引用计数减1。如果内存之前引⽤计数为4,autorelease之后仍然为4,未来某个时刻会变为3。(延迟一段时间)
-autoreleasepool :向⼀个对象发送autorelease消息,这个对象何时释放,取决于 autoreleasepool。
2)代码实例
// 1.创建Person对象
// alloc将对象的引用计数器从0到1,从无到有
Person *person1 = [[Person alloc]init]; // 计数器1
// 改一下这里 Automatic Reference Counting 自动引用为 NO
NSLog(@"%lu",[person1 retainCount]);
// 2.使用新对象再持有一次
// retain:将 对象 的引用计数器 +1
Person *person2 = [person1 retain]; //2
NSLog(@"%lu",[person2 retainCount]);
//不管有没有接收,只要给对象发送retain消息, 对象的引用计数器 +1
[person1 retain]; //3
NSLog(@"%lu",[person2 retainCount]);
// 3. 将引用计数器 -1
[person1 release];
NSLog(@"%lu",[person2 retainCount]); //针对对象使用的,任何的对象都可以 //2
[person1 release];
NSLog(@"%lu",[person2 retainCount]);//1
[person1 release], person1 = nil, person2 = nil; //person1 = nil,安全释放,指针置为nil
NSLog(@"%lu",[person2 retainCount]);//1 内存已经被收回了
// 自动释放池
NSAutoreleasePool *autoreleaselPool = [[NSAutoreleasePool alloc]init];
Person *p1 = [[Person alloc]init];//1
[p1 retain];//2
NSLog(@"%lu",p1.retainCount);
//延迟释放
[p1 autorelease];
NSLog(@"%lu",p1.retainCount); //2
//销毁自动释放池
[autoreleaselPool release];
NSLog(@"%lu",p1.retainCount); //1
//iOS5.0之后, 可以写 @autoreleasepool { }这种语法来表示自动释放
@autoreleasepool {
Person *p2 = [[Person alloc]init];
NSLog(@"%@",p2);
[p2 autorelease];
Person *p3 = [[Person alloc]init];
NSLog(@"%@",p3);
[p3 autorelease];
}
三、dealloc⽅法 对象死了属性消失
dealloc是继承自父类的方法,当对象引用计数为0的时候,由对象自动调用。
2)实例代码
- (void)dealloc
{
NSLog(@"%@ 被释放了", self);
// 使用从父类继承过来的方法,销毁从父类继承过来的属性或者实例变量
[super dealloc];
}
四、内存管理的基本原则。
凡是使⽤了alloc、retain或者copy让内存的引用计数增加了,就需要使用release或者autorelease让内存的引用计数减少。在一段代码内,增加和减少的次数要相等。
五、掌握copy的实现。 克隆 互不影响
一个对象想要copy,生成⾃己的副本,需要实现 NSCopying协议,定义copy的细节(如何copy)。如果类没有接受NSCopying协议而给对象发送copy消息,会引起crash。
Person.h文件中
//要想实现对象的拷贝,第一步是在文件.h中遵守<NSCopying>协议
@interface Person : NSObject<NSCopying>
@property (nonatomic , copy) NSString *name;
@property (nonatomic, assign) NSInteger age;
// 2实现<NSCopying>协议中的方法, copyWithZone:
- (id)copyWithZone:(NSZone *)zone
{
// 创建一个和person一模一样的对象返回
Person *p = [[Person allocWithZone:zone] init];
p.name = self.name;
p.age = self.age;
return p;
}
Person *p2 = [[Person alloc]init];
p2.name = @"大丫头";
p2.age = 19;
Person *p = [p2 copy];
NSLog(@"%@ %ld",p.name,p.age);