若排版有错误,请查看PDF版:http://pan.baidu.com/s/1sjPS0Zr
第四章 内存管理
一、 基本原理
1. 什么是内存管理
1) 当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间。比如回收一些不需要使用的对象、变量等
2) 管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效
2. 对象的基本结构
1) 每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”,即有多少人正在使用这个OC对象。
2) 每个OC对象内部专门有4个字节的存储空间来存储引用计数器。
3. 引用计数器的作用
1) 当使用alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1。
2) 当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收。
4. 引用计数器的操作
1) [p retainCount]; // 获取当前的计数器
// NSLog(@"%d", [p retainCount]);
2) [p retain]; // 计数器+1,retain方法返回的是对象本身。
3) [p release]; // 计数器-1,没有返回值。
4) dealloc
当一个对象要被回收的时候,就会调用
重写dealloc方法时,一定要调用[super dealloc],这句调用要放在最后面。
5. 对象的销毁
1) 当一个对象的引用计数器值为0时,它将被销毁,其占用的内存被系统回收
2) 当一个对象被销毁时,系统会自动向对象发送一条dealloc消息
3) 一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言
4) 一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用
5) 不要直接调用dealloc方法
6) 一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)
EXC_BAD_ACCESS : 访问了一块坏的内存(已经被回收、已经不可用的内存)
p = nil; // 空指针:没有指向任何东西的指针,给其发送消息不会报错。
野指针:指向僵尸对象(不可用内存)的指针。
// 当一个Person对象被回收的时候,就会自动调用这个方法
- (void)dealloc
{
[_car release]; // 一定要对当前(self)多拥有的所有对象做一次release。
[super dealloc]; // super的dealloc一定要调用,而且放在最后面
}
二、 内存管理原则
1. 谁alloc,谁release
2. 想使用(占用)某个对象,就应该让对象的计数器+1(让对象做一次retain操作)
3. 不想再使用(占用)某个对象,就应该让对象的计数器-1(让对象做一次release)
4. 谁retain,谁release
@implementation Person
- (void)setBook:(Book*)book
{
_book = [book retain];
}
- (Book *)book
{
return _book;
}
- (void)dealloc
{
[_book release];
NSLog(@"Person对象被回收");
[super dealloc];
}
@end
int main()
{
Book *b = [[Book alloc]init];
Person *p1 = [[Personalloc] init];
[p1 setBook:b]; //p1想占用b这本书
[p1 release];
[b release];
return 0;
}
三、 内存管理代码规范
1. 只要调用了alloc,必须有release(autorelease),对象不是通过alloc产生的,就不需要release
2. set方法的代码规范
1) 基本数据类型:直接复制
- (void)setAge:(int)age
{
_age = age;
}
2) OC对象类型
- (void)setCar:(Car *)car
{
if ( car != _car ) // 1.先判断是不是新传进来对象
{
[_car release]; // 2.对旧对象做一次release
_car = [car retain]; // 3.对新对象做一次retain
}
}
3. dealloc方法的代码规范
1) 一定要[super dealloc],而且放到最后面
2) 对self(当前)所拥有的其他对象做一次release
- (void)dealloc
{
[_carrelease];
[super dealloc];
}
四、 @property参数
1. set方法内存管理相关参数
1) retain : release旧值,retain新值(适用于OC对象类型)
2) assign : 直接赋值(默认,适用于非OC对象类型)
3) copy : release旧值,copy新值
2. 是否生成setter
1) readwrite : 同时生成setter和getter的声明、实现(默认)
2) readonly : 只会生成getter的声明、实现
3. 多线程管理
1) nonatomic : 性能高 (一般就用这个)
2) atomic : 性能低(默认)
4. setter、getter方法的名称
1) setter : 决定了set方法的名称,一定要有个冒号:
2) getter : 决定了get方法的名称(一般用在BOOL类型)
5. OC对象类型
@property (nonatomic, retain) 类名 *属性名;
@property (nonatomic,retain) Car *car;
@property (nonatomic,retain)id car;
@property (nonatomic,retain) NSString *name;
6. 非OC对象类型(int/float/enum/struct)
@property (nonatomic,assign) 类型名称 属性名;
@property (nonatomic,assign)int age;
@property (nonatomic,assign,readonly)int weight;
@property (getter = isRich)BOOL rich;//返回BOOL类型的方法名一般以is开头
五、 循环引用
循环引用时用@Class
1. @Class的作用
仅仅是告诉编译器,某个名称是一个类。
@class Person; // 仅仅告诉编译器,Person是一个类
2. 开放中引用一个类的规范
1) 在.h文件中用@class来声明类
2) 在.m文件中用#import来包含类的所有东西
3. 两端循环引用的解决方案
1) 一端用retain
2) 一端用assign
Person.h | Card.h |
@class Card; // #import “Card.h” // @class仅仅是告诉编译器,Card是一个类 @interface Person : NSObject @property (nonatomic,retain) Card *card; @end | @class Person; @interface Card : NSObject @property (nonatomic,assign) Person *person; @end |
main.m | |
#import <Foundation/Foundation.h> #import "Card.h" #import "Person.h" int main() { Person *p = [[Person alloc] init]; Card *c = [[Card alloc] init]; p.card = c; c.person = p; [c release]; [p release]; return 0; } |
六、 autorelease
1. autorelease的基本用法
1) 会将对象放到一个自动释放池中,池子放在栈中
2) 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
3) 会返回对象本身
4) 调用完autorelease方法后,对象的计数器不变
2. autorelease的好处
1) 不用再关心对象释放的时间
2) 不用再关心什么时候调用release
3. autorelease的使用注意
1) 占用内存较大的对象不要随便使用autorelease
2) 占用内存较小的对象使用autorelease,没有太大影响
int main()
{
@autoreleasepool
{// { 表示开始,创建释放池
// autorelease方法会返回对象本身
// 调用完autorelease方法后,对象的计数器不变
// autorelease会将对象放到一个自动释放池中
// 当自动释放池被销毁时,会对池子里面的所有对象做一次release操作
Person *p =[[[Person alloc] init] autorelease];
p.age = 10;
@autoreleasepool
{
Person *p2 =[[[Person alloc] init] autorelease];
p2.age = 10;
}
} // } 表示结束,销毁释放池
}
4. 错误写法
1) alloc之后调用了autorelease,又调用release
@autoreleasepool
{
Person *p = [[[Person alloc] init] autorelease];
[p release];
}
2) 连续调用多次autorelease
@autoreleasepool
{
Person *p = [[[[Person alloc] init] autorelease] autorelease];
}
5. 自动释放池
1) 在iOS程序运行过程中,会创建无数个池子。这些池子都是以栈结构存在(先进后出)
2) 当一个对象调用autorelease方法时,会将这个对象放到栈顶的释放池
6. 使用注意
1) 系统自带的方法里面没有包含alloc、new、copy,说明返回的对象都是autorelease的
2) 开发中经常会提供一些类方法,快速创建一个已经autorelease过的对象,创建对象时不要直接用类名,一般用self
+ (id)person
{
return [[[self alloc] init] autorelease];
}
七、 ARC
1. 基本简介
l ARC是自iOS 5之后增加的新特性,完全消除了手动管理内存的烦琐,编译器会自动在适当的地方插入适当的retain、release、autorelease语句。你不再需要担心内存管理,因为编译器为你处理了一切
l ARC 是编译器特性,而不是 iOS 运行时特性,它也不是类似于其它语言中的垃圾收集器。因此 ARC 和手动内存管理性能是一样的,有时还能更加快速,因为编译器还可以执行某些优化
2. 基本原理
1) 规则
只要没有强指针指向对象,就会释放对象。
2) 强指针、弱指针
1) 强指针:默认情况下,所有的指针都是强指针 __strong
2) 弱指针:__weak
3. ARC特点
1) 不允许调用release、retain、retainCount
2) 允许重写dealloc,但是不允许调用[super dealloc]
3) @property的参数
strong :成员变量是强指针(适用于OC对象类型)
weak :成员变量是弱指针(适用于OC对象类型)
assign : 适用于非OC对象类型
4) 以前的retain改为用strong
5) 当循环引用的时候
a) ARC:一端用strong,一端用weak
b) 非ARC:一端用retain,一端用assign
Dog.h | Person.h |
#import <Foundation/Foundation.h> @class Person; @interface Dog : NSObject @property (nonatomic,weak) Person *person; @end | #import <Foundation/Foundation.h> @class Dog; @interface Person : NSObject @property (nonatomic,strong) Dog *dog; @end |
main.m | |
#import <Foundation/Foundation.h> #import "Person.h" #import "Dog.h"
int main() { Person *p = [[Person alloc] init]; Dog *d = [[Dog alloc] init]; p.dog = d; d.person = p; return 0; } |