内存区域
1.堆区:需要的时候系统会为你分配内存,但是系统不会自动回收,需要程序员手动释放内存
2.栈区:需要的时候系统会为你分配内存,不需要的时候系统自动回收该内存
3.常量区:存储常量,数据不能修改
4.全局,静态区域:存储全局变量和静态变量
5.自由存储区
内存管理的原理
保证每个对象在使用的时候存在于内存中,不用的对象在最后从内存中清除。
一个对象的生命周期:
1.对象的初始化
2.执行,使用
3.释放
对象使用内存结束后,需要释放自身所占的资源,归还给系统,以保证别的对象可以使用。
注意:内存管理只针对继承NSObject的对象,对其他的基本数据类型(int,float,double...)无效。
本质原因是因为对象和其他数据类型在系统中的存储空间不一样,其它局部变量主要存放于栈中,而对象存储于堆中,当代码块结束时这个代码块中涉及的所有局部变量会被回收,指向对象的指针也被回收,此时对象已经没有指针指向,但依然存在于内存中,造成内存泄露。
retainCount
在引用计数中,每一个对象负责维护对象所有引用的计数值。当一个新的引用指向对象时,引用计数器就递增,当去掉一个引用时,引用计数就递减。当引用计数到零时,该对象就将释放占有的资源。
1.每个OC对象都有自己的引用计数器,retainCount
2.当对象被创建时,引用计数置为1(alloc、copy或则new),注意是置为1,不是加1
3.使对象的引用计数+1,并且获得对象的所有权;
4.使对象的引用计数-1,并且放弃对象的所有权;
5.当引用计数值为0的时候,对象将被系统销毁。
那OC中,使用什么来控制对象的引用计数呢?
retainCount:获取对象当前的引用计数值;
alloc:返回一个对象,并将其引用计数设置为1,类方法;
retain:将对象的引用计数加1;
release:将对象的引用计数减1;
autorelease:将对象加入自动释放池,对象引用计数滞后减1;
除了上述方法之外,还有对象销毁的方法dealloc,我们不会主动调用,但是需要在类中重写;
//自动走,无需调用
-(void)dealloc
{
NSLog(@"person dealloc");
[_book release]; //self.book = nil;
[_name release];
[super dealloc];
}
多个对象的内存管理
.h文件中
#import<Foundation/Foundation.h>
#import"Book.h"
@interfacePerson : NSObject
{
Book *_book;
NSString *_name;
}
-(void)setBook:(Book *)book;
-(Book *)book;
.m文件中
-(void)setBook:(Book *)book
{ //判断旧的和新的是否是同一个
if(_book != book){
//释放旧的
[_book release];
//持有新的
_book = [book retain];
}
}
-(Book *)book
{
return _book;
}
main.m文件中
//某个人拥有一本书
Person *person = [[Person alloc]init];
Book *myBook = [[Book alloc]init];
Book *myBook1 = [[Book alloc]init];
person.book = myBook; //要获取新的对象,引用计数要为2,所以在set方法中写
person.book = myBook1;
[person release];
[myBook release];
自定义初始化方法
.h文件中
-(instancetype)initWithBook:(Book *)book name:(NSString *)name;
.m文件中
-(instancetype)initWithBook:(Book *)book name:(NSString *)name
{
self = [super init];
if (self) {
//因为是初始化,所以不需要在前面加release
_book = [book retain];
_name = [name retain];//对应的release在dealloc里面
}
return self;
}
集合
1.某个对象,加入到数组中,retainCount + 1,不影响数组的retainCount
2.数组的retainCount改变不影响数组里面的元素的retainCount
3.数组对象被销毁,里面所有的对象元素,retainCount -1
集合都是同理
Person *person = [[Person alloc]init];
NSArray *array = [[NSArrayalloc]initWithObjects:@"1",@"2",person, nil];
NSLog(@"per = %lu,arr =%lu",person.retainCount,array.retainCount);
[array retain];
NSLog(@"per = %lu,arr =%lu",person.retainCount,array.retainCount);
[array release];
[array release]; //数组没有了,所有的元素都会release一次,所以person只需release一次
[person release];
自动释放池
NSAutoreleasePool:自动释放池类或对象
类似于一个容器,所有加入容器中的对象都被他管理,在自动释放池,自身销毁的时候,池将会释放(release)所有的对象
autorelease 方法把对象加入池中,一个对象可以被多次autorelease
便利初始化的对象默认已经加入当前池中
NSAutoreleasePool*pool = [[NSAutoreleasePool alloc] init];
//pool 1
Person *person = [[Person alloc]init];//1
[person retain];//2
NSLog(@"person = %ld",[personretainCount]);
[person retain];//3
NSLog(@"person = %ld",[personretainCount]);
[person autorelease];//3
NSLog(@"person = %ld",[personretainCount]);
[person autorelease];//3
NSLog(@"person = %ld",[personretainCount]);
[person autorelease];//3
NSLog(@"person = %ld",[person retainCount]);
[pool release]; //person = 0(1)
NSLog(@"person =%ld",[person retainCount]);
便利构造方法
NSArray*array = [[[NSArray alloc]initWithObjects:@"1", nil]autorelease];
//等价于上面的alloc+autorelease,系统的便利构造方法不需要内存管理
NSArray * array1 = [NSArrayarrayWithObjects:@"2", nil];
自定义便利构造法
Book *book = [[Book alloc]init];
Person*person = [Person PersonWithBook:book name:@"lili"];
[book release];
属性的内存管理
@property (nonatomic,retain)Book*book; //用属性就不用再进行set方法重写的内存管理,重写dealloc就行了
循环引用
//让其中一个属性变为弱引用,在相互导入的时候,最好用@classPerson,如果要使用类中的属性和方法,需要在.m导入#import
Book *book = [[Book alloc]init];
Person *person = [Person PersonWithBook:book name:@"lili"];
person.book = book;
book.person = person;
[book release];
//在ARC状态下,如果是对象类型 retain,copy – strong
//weak
//MRC状态下,不需要持有对象 assign ,需要持有对象 retain,copy
NSString *string = @"hello";
NSLog(@"%ld",string.retainCount);
NSString *string1 = [[NSString alloc]initWithFormat:@"你好"];
[string release]; //建议写
NSLog(@"string1 = %ld",string1.retainCount);
异常状况
//1、混乱释放
Person * person = [[Person alloc] init];
Person * person1 = person;
[person1 release];//不遵守内存管理原则
[person release] 较标准
//2、内存泄露
Person * person1 = [[Person alloc] init];
Person * person2 = [[Person alloc] init];
person2 = person1;//指针指向发生改变,person2原有内存 泄露, 解决方案 autorelease
//3、过度释放
Person * person = [Person personWithName:@"tom" age:12];
[person release];//便利构造器初始化的对象,过度释放了,不需要release
//4、nil对象的引用计数为0
Person * person = nil;
NSLog(@"%lu", [person retainCount]);
//5、常量对象的引用计数为无穷
NSString * name = @"name";
NSLog(@"%lu", [name retainCount]);
//name对象放在常量区
//6.针对NSString对象
NSString *string = [[NSString alloc] initWithFormat:@"213"];
NSLog(@"string = %ld",[string retainCount]);
//在堆中分配内存,都用引用计数模式,该string对象是常量区,用%d打印是-1,%lu就是正无穷
NSMutableString *mutableString = [[NSMutableString alloc]initWithFormat:@"111"];
NSLog(@"mutalbe = %ld",[mutableString retainCount]);
//nsmutableString创建的对象,是在堆中分配内存的,遵循引用计数模式