一、内存管理的方式
造成程序崩溃的主要原因:
1. 野指针:指针所指向的空间已经被回收,但是依然使用该指针访问已经被收回的空间(野指针不会立刻造成程序崩溃)。
2. 内存溢出:所需内存超过了给定内存的上限。
3. 内存泄漏:内存已经被开辟,但是没有被释放。
iOS支持两种内存管理方式:ARC和MRC。
MRC的内存管理机制是:引用计数。ARC是基于MRC的。
ARC与MRC的区别:
MRC:手动引用计数,对象的创建以及对象的所有权的释放都需要我们自己去写代码操作,对我们自己的要求较高。
ARC:iOS5.0之后出现的,是编译器特性,系统会在合适的位置自动的帮我们添加上release/autorelease代码,我们不需要自己去写代码释放所有权,ARC不是垃圾回收,本质还是MRC。
二、引用计数机制,影响引用计数的各种方法
OC采用引用计数机制管理内存,每个对象都有一个引用计数器,用来记录当前对象的引用次数。当一个新的引用指向对象时,引用计数器就加1,当去掉一个引用时,引用计数就减1。当引用计数到零时,该对象的空间就被系统回收。
影响引用计数的方法:
//alloc 使应用计数 0---1.
//retain 使引用计数在原有的基础上+1
Person *p2 = [p1 retain];
NSLog(@"%lu", [p2 retainCount]);
Person *p3 = [p2 retain];
NSLog(@"%lu", [p3 retainCount]);
//注意:引用计数指的是开辟的空间的引用计数,不是指针变量的引用计数,指针变量是没有引用计数的.
//release 在引用计数原有的基础上-1(立刻减1).
//autorelease 延迟释放,必须和自动释放池结合使用.也是使对象的引用计数-1,但是会在出了离其最近的自动释放池后将引用计数-1
[p3 release];
NSLog(@"%lu", [p2 retainCount]);
[p2 autorelease];
NSLog(@"%lu", [p1 retainCount]);
[p1 release];
//dealloc 回收对象的空间,当对象的引用计数变为0时,该方法会自动调用.如果想查看对象的空间是否被回收,可以重写该方法,但是注意需要调用super.
注意:永远不要手动的调用dealloc
//copy:新创建一个对象,引用计数由0变成1,原对象的引用计数不发生变换.
对象的引用计数只有在堆区才是有意义的。
三、内存管理的基本原则
只要使用了alloc,retain或者copy让内存的引用计数增加了,就需要使用release或者autorelease让内存的引用计数减少。在一段代码内,增加和减少的次数要相等。
如果增加的次数大于减少的次数,会造成内存泄漏。
如果增加的次数小于减少的次数,会造成过度释放。
如果增加的次数等于减少的次数,还继续访问,造成野指针问题。
四、协议
协议:本质上就是一堆方法的声明,哪个类遵循该协议,该类就需要实现协议中的方法。一个类可以遵循多个协议,每个协议用逗号隔开。
协议中的方法分两种:必须实现的,用@required关键字修饰。默认的,选择实现的,用@optional关键字修饰。
实现一个基础版的协议流程:
1. 创建一个协议(.h文件)。
2. 声明协议中的方法。
3. 让某个类遵循协议(导入协议文件,在该类父类名的后面写<协议名>)。
4. 该类遵循协议后需要实现协议中的方法。
5. 创建该类对象,执行协议方法。
示例:
协议文件:
@protocol MyProtocol <NSObject>
@required
- (void)sayHello;
@optional
- (void)sayBye;
@end
.h文件:
#import "MyProtocol.h"
@interface Person : NSObject<MyProtocol>
@property (nonatomic, copy) NSString *name;
@end
.m文件实现协议方法:
- (void)sayHello
{
NSLog(@"你好,妹子,我是%@,请问,这是你掉的板砖吗?", self.name);
}
- (void)sayBye
{
NSLog(@"再见,妹纸~~~~下次再XXXX");
}
五、内存拷贝
拷贝:如果自定的类想要实现copy,必须遵循NSCopying协议,并实现协议中的方法,才可以实现copy,不然会crash。
根据NSCoping中copyWithZone:方法的实现不同,拷贝分为三种类型:
1. 伪拷贝:拷贝地址,相当于retain操作,引用计数加1。
- (id)copyWithZone:(NSZone *)zone
{
return [self retain];
}
2. 浅拷贝:对象开辟新的空间,但是两个对象的实例变量指向同一块空间。
- (id)copyWithZone:(NSZone *)zone
{
Person *p = [[Person allocWithZone:zone] init];
return p;
}
3. 深拷贝:对象开辟新的空间,两个对象的实例变量也指向不同的空间。
- (id)copyWithZone:(NSZone *)zone
{
Person *p = [[Person allocWithZone:zone] init];
p.name = [self.name mutableCopy];
return p;
}