------- android培训、java培训、iOS培训、.Net培训、期待与您交流! ----------
为什么要内存管理?
移动设备的内存及其有限,每个app所能占用的内存是有限制的。当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要使用的内存空间。否则程序会卡死,甚至崩溃。因此必须学会内存管理。
Objective-C提供了三种内存管理方式:
MannulReference Counting(MRC,手动管理, iOS4.1之前的版本)
automatic reference counting(ARC,自动引用计数,iOS4.1 之后推出的)
garbage collection(垃圾回收)。iOS不支持垃圾回收;
ARC作为苹果新提供的技术,苹果推荐开发者使用ARC技术来管理内存;
开发中如何使用:需要理解MRC,但实际使用时尽量用ARC
内存管理的范围:
任何继承了NSObject的对象(所有的OC对象,对象类型)
对其他非对象类型(基本数据类型)无效(int、char、float、double、struct、enum等)
只有OC对象才需要进行内存管理的本质原因:
因为OC对象存放于堆里面,非OC对象一般放在栈里面(栈内存会被系统自动回收)
内存管理的原理:
在每个对象内部,都有一个4个字节的引用计数器,表示“对象的引用个数”,当用new,alloc,copy创建一个新对象后,计数器的值为1。当对象调用retain方法时,计数器加1,当调用release方法是,计数器减1。当计数器的值减为0后,对象会被回收。在对象被销毁之前,系统会向对象发送一条dealloc消息,此时系统就会帮我们释放该对象所占用的内存。
1、引用计数器的常见操作
给对象发送一条retain消息, 可以使引用计数器值+1(retain方法返回对象本身)
Person *p = [Person alloc]init];
[p retain];
给对象发送一条release消息, 可以使引用计数器值-1
[p release];
给对象发送retainCount消息, 可以获得当前的引用计数器值(通过%ld输出查看)
NSLog(@"p->retainCount = %ld",p.retainCount);
注意: release并不代表销毁\回收对象, 仅仅是计数器-1
我们只能通过操作对象计数器,间接控制对象的释放与否。
以上这些操作必须在关闭ARC的情况下才能使用,Xcode默认是开启ARC。
2、关闭ARC:
1.设置项目信息
2.设置ARC为NO
3、判断对象是否被回收
1)一定要[super dealloc],而且要放到最后,意义是:先释放子类占用的空间再释放父类占用的空间
2)对self(当前)所拥有的的其他对象做一次release操作
-(void)dealloc
{
[_car release];
[super dealloc];
}
注意
1)永远不要直接通过对象调用dealloc方法(实际上调用并不会出错)
2)一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)。为了防止调用出错,可以将“野指针”指向nil(0)。
#import <Foundation/Foundation.h>
@interface Person : NSObject
@property int age;
@end
Person.m
#import "Person.h"
@implementation Person
//监测对象是否被回收的方法
-(void)dealloc{
//释放子类对象
NSLog(@"对象被销毁了~");
//释放父类
[super dealloc];
}
@end
main.m
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
//用Person 类实例化一个实例对象
Person *p = [Person new]; // 对象有没有所有者? 有
//证明有一个所有者
NSUInteger count = p.retainCount;
NSLog(@"count = %lu",count); // 1
//使用引用计数器+1
[p retain];
NSLog(@"p.retainCount = %lu",p.retainCount); //2
//如果要回收对象? 应该想办法 retatinCount = 0
[p release];
NSLog(@"p.retainCount = %lu",p.retainCount); //1
[p release]; //此处执行后,p的空间被回收 //0
//证明p的空间被释放了,可以在在Person类中,重写dealloc方法
}
return 0;
}
内存管理的原则
1)原则
只要对象还有人在使用,那么这个对象就不会被回收;只要你想使用这个对象,那么就应该让这个对象的引用计数器+1;当你不想使用这个对象时,应该让对象的引用计数器-1;
2)“谁污染,谁治理。谁开发,谁保护”,跟这句口号一样,内存是,谁创建,谁release。谁retain,谁release。
(1)如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者autorelease方法
(2)只要你调用了retain,无论这个对象时如何生成的,你都要调用release
单个对象的内存管理(野指针)
野指针访问:访问了一块坏的内存(已经被回收的,不可用的内存)。
僵尸对象:所占内存已经被回收的对象,僵尸对象不能再被使用。(默认情况下xcode为了提高编码效率,没有开启僵尸对象检测)
Person *p = [Person alloc]init];// 1
[p release]; // 0
//此时p为野指针,也称之为僵尸对象
[p run];//野指针访问
[p retain];//僵尸对象死而不能复生
打开僵尸对象检测
Person *p = [Person alloc]init];// 1
[p release]; // 0
p = nil;
[p run];//相当于[nil retain],空指针调用方法无反应
[p retain];//相当于[nil retain],不会报错,但应避免
多个对象内存管理(野指针)
#import <Foundation/Foundation.h>
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *p = [[Person alloc]init];// 1
Car *bmw=[Car new];// 1
bmw.speed=80;
// [p setCar:bmw];//可以用点语法替换
p.car=bmw;
NSLog(@"bmw retainCount=%lu",[bmw retainCount]);
[p goLasa];
[p goLasa];
[bmw release];//_car--->1
NSLog(@"bmw retainCount=%lu",[bmw retainCount]);
// [bmw release];//将此句放在人的dealloc方法里,可以让车先于人亡,同时不影响人的方法的调用,否则会出现人没车还能开车的不合理现象
[p goLasa];
[p release];
}
return 0;
Car.h
#import <Foundation/Foundation.h>
@interface Car : NSObject
{
int _speed;
}
-(void)setSpeed:(int)speed;
-(void)run;
@end
Car.m
@implementation Car
- (void)dealloc
{
NSLog(@"车已经毁了");
[super dealloc];
}
-(void)setSpeed:(int)speed{
_speed=speed;
}
-(void)run{
NSLog(@"小明正开车在以%d码的速度去拉萨",_speed);
}
@end
Person.h
#import <Foundation/Foundation.h>
#import "Car.h"
@interface Person : NSObject
{
Car *_car;
}
-(void)setCar:(Car*)car;
-(void)goLasa;
@end
Person.m
#import "Person.h"
@implementation Person
- (void)dealloc
{
[_car release];//在人挂掉之前让车报废 _car--->0
NSLog(@"人已经挂了");
[super dealloc];
}
-(void)setCar:(Car*)car{
_car=[car retain];//_car--->2
}
-(void)goLasa{
[_car run];
}
@end