内存管理方式
1.垃圾回收机制(Garbage Collection)有系统管理内存,开发人员不需要管理
2.oc从2.0以后,就开始之初垃圾回收机制,但是用于oc开发(苹果电脑的操作系统),我们的ios平台是不支持垃圾回收机制
不支持垃圾回收机制,oc是如何管理内存的呢?
内存不释放就叫内存泄露,oc通过引用计数器管理内存
根据照明设备所做的动作分析:
(1)图中的行为 开灯 需要照明 不需要照明 关灯
(2)对象的操作 生成对象 持有对象 释放对象 对象销毁
(3)oc中的方法 alloc/copy/new retain release/autorelease dealloc
3.图中需要照明的人数就代表引用计数
(1)当第一个人需要照明时,引用计数 +1 0—>1
(2)当第二个人需要照明时,引用计数 +1 1—>2
(3)当第三个人需要照明时,引用计数 +1 2—>3
(4)当有一个人不需要照明,引用计数 -1 3—>2
(5)当再有一个人不需要照明,引用计数-1 2—>1
(6)当再由一个人不需要照明,引用计数-1 1—>0
4.ios平台下使用引用器如何管理内存,有两种方式
(1)MRC:(Manual Reference Count)手动引用计数,有开发人员通过手动引用计数器来管理内存
(2)ARC:(Automatic Reference Count)自动引用计数,有系统通过引用计数管理内存
5.学习好MRC是为了更好地使用ARC,ios5.0以后就有了ARC
ARC和MRC
(1)如果工程环境是ARC,而部分文件使用了MRC,使用一个参数-fno-objc-arc
(2)如果工程环境MRC,而部分文件使用了ARC,使用一个参数-fobjc-arc
6.自己创建的对象自己持有
Person
*p= [[Person
alloc]init];
alloc会造成对象的引用计数由0-->1
7.如何判断一个对象是否被持有,就看他的引用计数判断对象要不要回收的唯一依据就是计数器是否为0,若不为0则存在。
retainCount:对象的引用计数
NSLog(@"%lu",p.retainCount);
8.如果自己创建的对象不使用时,释放对象使用release
[p
release];//1-->0
[p retain];不能使用[p retaion]让僵尸对象起死复生。
[p release];不要过度释放,过度释放,也等于操作僵尸对象
9.retain可以让对象的引用计数加1
非自己创建的对象,自己也能持有
10.方法的基本使用
(1)retain 造成引用计数加1,会返回对象本身
(2)release 造成引用计数减1,没有返回值
(3)retainCount的作用:获取当前对象的计数值
(4)当一个对象被销毁的时候,系统自动调用,在此方法中一定要调用[super delloc]这句话要放到最后
11.概念:
野指针错误:访问了一块坏的内存(已经被回收的,不可用的内存)。
僵尸对象:所占内存已经被回收的对象,僵尸对象不能再被使用。(打开僵尸对象检测)
空指针:没有指向任何东西的指针(存储的东西是0,null,nil),给空指针发送消息不会报错
注意:不能使用[p retaion]让僵尸对象起死复生。
内存管理原则
1.原则
(1)只要还有人在使用某个对象,那么这个对象就不会被回收
(2)只要你想使用这个对象,那么就应该让这个对象的引用计数器加1
(3)当你不想使用这个对象时,应该让对象的引用计数器减1
2.谁创建,谁release
(1)如果你通过alloc,new,copy来创建了一个对象,那么你就必须调用release或者autorelease方法
(2)不是你创建的就不用你去负责
3.谁retain,谁release
只要你调用了retain,无论这个对象如何生成的,你都要调用release
4.总结
有始有终,有加就应该有减,曾经让某个对象计数器加1,就应该让其在最后减1
5. [[Person alloc]init];//这种情况,内存永远都不会被释放
[[[Person alloc]init]release];//创建的同时立即被释放掉
autorelease相比release,也能造成引用器-1,不过autorelease不是立即-1,会在未来的某段时间进行-1操作
6. autorelease执行的实质:对一个对象使用autorelease操作,这个对象的引用器不会立即-1;如果你把这个autorelease操作放到自动释放池中,会在出池子的一瞬间-1;如果你没有放到池子中,不会对autorelease操作对象进行-1;
注意:你要使用autorelease必须和自动释放池配合使用
7.创建自动释放池的两种形式
(1)NSAutoreleasePool自动释放池类
NSAutoreleasePool
*pool =[[NSAutoreleasePool
alloc]init];
Person *p6 =[[Person alloc]init];
p6.name = @"白子画";
[p6 autorelease];
NSLog(@"%lu",p6.retainCount);
[pool release];
Person *p6 =[[Person alloc]init];
p6.name = @"白子画";
[p6 autorelease];
NSLog(@"%lu",p6.retainCount);
[pool release];
(2)第二种形式
@autoreleasepool
{
Person *p7 =[[Person alloc]init];
p7.name = @"糖宝";
[p7 autorelease];//未来的时间-1,出自动释放池-1
// [p7 release];//1--->0
NSLog(@"%lu",p7.retainCount);
Person *p7 =[[Person alloc]init];
p7.name = @"糖宝";
[p7 autorelease];//未来的时间-1,出自动释放池-1
// [p7 release];//1--->0
NSLog(@"%lu",p7.retainCount);
}//此时也会引起过度释放,是由autorelease引起的
8.
NSMutableArray *mArray=[[NSMutableArray
alloc]initWithCapacity:0];
//网数组中添加十个人
for (int i = 0; i < 10; i++) {
Person *person =[[Person alloc]init];
person.name =[NSString stringWithFormat:@"%d",i];
//数组在添加元素的时候,会对添加的元素做一次retain操作
[mArray addObject:person];
kRelease_Safe(person);
}
NSLog(@"%@",mArray);
//数组在释放自己之前,会对之前添加到数组中的元素进行引用计数减一的操作
kRelease_Safe(mArray);
//此时我们没有alloc也没有retain,所以不需要对数组mArray2的内存释放负责
NSMutableArray *mArray2 =[NSMutableArray array];
Person *p10 =[[Person alloc]init];
p10.name = @"朱然";
[mArray2 addObject:p10];
//网数组中添加十个人
for (int i = 0; i < 10; i++) {
Person *person =[[Person alloc]init];
person.name =[NSString stringWithFormat:@"%d",i];
//数组在添加元素的时候,会对添加的元素做一次retain操作
[mArray addObject:person];
kRelease_Safe(person);
}
NSLog(@"%@",mArray);
//数组在释放自己之前,会对之前添加到数组中的元素进行引用计数减一的操作
kRelease_Safe(mArray);
//此时我们没有alloc也没有retain,所以不需要对数组mArray2的内存释放负责
NSMutableArray *mArray2 =[NSMutableArray array];
Person *p10 =[[Person alloc]init];
p10.name = @"朱然";
[mArray2 addObject:p10];
kRelease_Safe(p10);
9. NSString
*string =[[NSString
alloc]initWithString:@"12345678912345667788999"];
NSLog(@"%ld",string.retainCount);
//使用stringwithFormat创建的字符串对象,当长度小于10是时候在常量区,大于10时候在堆区 stringwithFormat创建字符串引用计时器是1 ,其他的创建都是-1
//使用其他方法创建的字符串都在常量区
NSString *string2 =[NSString stringWithFormat:@"1234567890"];
NSLog(@"%ld",string.retainCount);
//使用stringwithFormat创建的字符串对象,当长度小于10是时候在常量区,大于10时候在堆区 stringwithFormat创建字符串引用计时器是1 ,其他的创建都是-1
//使用其他方法创建的字符串都在常量区
NSString *string2 =[NSString stringWithFormat:@"1234567890"];
NSLog(@"%lu",string2.retainCount);
copy的实现
1.copy 对象能够使用copy操作的,前提是NSCopy协议
前拷贝,之拷贝地址,同时让对象的引用计数器+1,此时,拷贝出来的对象和源对象所占的空间相等,内容也相等
深拷贝:不止拷贝空间和内容,而且又重新开辟了一块内存
浅拷贝:会造成原对象的引用计数器加1,深拷贝不会造成原对象的引用计数器加1
2.
//dealloc方法是对象被销毁的时候自动调用的方法,不需要手动调用,由于delloc方法是重写的父类的方法,所以,再重写次方法时,一定要把父类方法的内容也写上
- (void)dealloc
{
NSLog(@"Person %@对象被销毁了",_name);
//以后重写delloc方法时 ,一定要调用[super dealloc]方法,且次方法一定要再最下面
- (void)dealloc
{
NSLog(@"Person %@对象被销毁了",_name);
//以后重写delloc方法时 ,一定要调用[super dealloc]方法,且次方法一定要再最下面
[super
dealloc];
3. -(id)copyWithZone:(NSZone
*)zone{
//返回对象的时候要作一次retain操作,外面的接收者才真正持有这个对象
//浅拷贝
// return [self retain];
//深拷贝:返回的是一个新的对象,此时两个对象所占的空间大小相同,内容相同
Person *p =[[Person allocWithZone:zone]init];
p.name = self.name;
return p;
//返回对象的时候要作一次retain操作,外面的接收者才真正持有这个对象
//浅拷贝
// return [self retain];
//深拷贝:返回的是一个新的对象,此时两个对象所占的空间大小相同,内容相同
Person *p =[[Person allocWithZone:zone]init];
p.name = self.name;
return p;
}
4. //new
创建对象的同时让其引用计数0-->1
//new其实就做了alloc和init两步操作,申请空间并初始化
// [Person new] = [[Person alloc]init];
Person *p13=[Person new];
p13.name = @"东方";
//new其实就做了alloc和init两步操作,申请空间并初始化
// [Person new] = [[Person alloc]init];
Person *p13=[Person new];
p13.name = @"东方";
kRelease_Safe(p13);
5.
内存管理的总结:
1.引用计数+1:(alloc Copy new retain)
2.引用计数-1:(release,autorelease)
3.要想内存不出现泄露,引用计数+1的次数要和引用计数-1的次数相等
4.一旦引用计数为零时,系统会自动调用dealloc方法
5.谁污染,谁治理
1.引用计数+1:(alloc Copy new retain)
2.引用计数-1:(release,autorelease)
3.要想内存不出现泄露,引用计数+1的次数要和引用计数-1的次数相等
4.一旦引用计数为零时,系统会自动调用dealloc方法
5.谁污染,谁治理