OC中堆区常见的三种错误:
1.过度释放:对一个对象释放了不止一次
2.野指针:访问没有所有权的内存,如果想要访问就必须要保证内存还在(也就是指针指向了一个无效的内存)
3.内存溢出:开辟的内存没有得到及时的释放,也就是在使用完内存之后没有及时释放内存
OC当中内存管理方式:ARC / MRC
ARC:自动引用计数(自动管理内存) 由开发人员开辟空间,但是由系统自动释放内存,本质还是基于MRC,这是系统默认的内存管理方式,也是苹果推荐使用的方式
MRC:手动引用计数(手动管理内存) 由开发人员开辟空间,在使用完对象之后由开发人员手动及时释放内存;;;;比起ARC的好处就是能够灵活地控制空间何时释放
内存管理机制:采用引用计数机制
影响引用计数的几个方法:
1.alloc 在堆区开辟内存,空间从无到有,引用计数由0到1
2.retain 将原有的对象的引用计数+1
3.copy 拷贝出一个新的对象,将新的对象引用计数+1,原有的对象的引用计数不变
4.release 将原有的对象的引用计数-1
5.autorelease 在将来的某一个时刻会将引用计数-1
#import
#import "Person.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Person *per = [[Person alloc] init];//此时per从0到1
//retaincount 记录当前对象的引用计数
NSLog(@"per = %lu",[per retainCount]);
Person *per1 = [per retain];//per 从1到2
NSLog(@"per = %lu",[per retainCount]);//per --- 2
NSLog(@"per = %lu",per.retainCount);//per --- 2
NSLog(@"per1 = %lu",per1.retainCount);//per1 ---- 2
[per1 release];
NSLog(@"per = %lu",per.retainCount);//per --- 1
NSLog(@"per1 = %lu",per1.retainCount);//per1 ---- 1
[per1 retain];
NSLog(@"per = %lu",per.retainCount);//per --- 2
NSLog(@"per1 = %lu",per1.retainCount);//per1 ---- 2
Person *per2 = [per1 retain];
NSLog(@"per = %lu",per.retainCount);//per --- 3
NSLog(@"per1 = %lu",per1.retainCount);//per1 ---- 3
NSLog(@"per2 = %lu",per2.retainCount);//3
[per release];
[per release];
[per release];//per ---- 0 对象的引用计数为零时,系统会自动调用dealloc方法,回收空间
//当代码执行到这里的时候,此时对象的空间已经被系统收回,如果以后再次使用per指针,则会出现野指针问题,所以,我们需要将指针per置为nil
per = nil;
per1 = nil;
per2 = nil;
NSLog(@"per = %lu",per.retainCount);
//为什么对象在销毁时本来该对象的引用计数为0,输出时反而为1 ?
//内存管理的几道练习题
Person *stu1 = [[Person alloc] init];// 0 --- 1
Person *stu2 = [[Person alloc] init];// 0 --- 1
stu2 = stu1;
NSLog(@"stu1 = %lu",stu1.retainCount);
NSLog(@"stu2 = %lu",stu2.retainCount);
[stu1 release];
//第二种情况
//在NSString当中,当使用 alloc initWithFormat:创建字符串对象的时候,如果是要在控制台Unicode编码输出的话,那就是对象指向堆区,如果是ASCII码表中,系统会根据字符串所占的空间的大小来分配空间,有可能不分配堆区内存,此时也就是对象可能指向常量区也可能指向堆区
NSString *str = [[NSString alloc] initWithFormat:@"MingGe"];
NSLog(@"str = %lu",str.retainCount);//如果对象指向的不是堆区空间的话,则输出一个无穷大的数值
//%lu --- 无穷大的数值
//%ld --- 输出-1
str = @"lanou";
NSLog(@"str = %ld",str.retainCount);
//什么时候才能满足引用计数概念?
//1.必须是对象调用
//2.对象必须指向堆区
//注意:如果指向了其他区的话则输出无穷大的数值
}
//手动添加一个自动释放池
@autoreleasepool {
//对象如果发送了autorelease消息,则autorelease过的对象会找离它最近的释放池,然后出了释放池,则自动释放池销毁时会找到autorelease过的对象,同时向对象发送一次release消息
Person *per2 = [[Person alloc] init];
[per2 autorelease];//调用这个会立马销毁
}
//copy的使用
//创建一个Person对象
Person *perOld = [[Person alloc] init];
//为属性赋值
perOld.name = @"XiaoLong";
perOld.sex = @"m";
perOld.age = 19;
perOld.score = 89;
//当对一个类的对象进行copy操作时,需要将该类服从遵守NSCopying协议,同时实现协议里的一个方法
//创建一个Person对象,接收拷贝出来的新的对象
Person *perNew = [perOld copy];
//copy拷贝 在堆区开辟一个新的空间,perNew的引用计数+1,perOld不变
//释放
[perOld release];
[perNew release];
NSLog(@"perNew name = %@ sex = %@ age = %lu score = %.2f",perNew.name,perNew.sex,perNew.age,perNew.score);
//置空
perNew = nil;
perOld = nil;
return 0;
}
Person.h
***************************
#import
@interface Person : NSObject<NSCopying>//遵守NSCopying协议
@property (nonatomic ,retain)NSString *name;
@property (nonatomic ,retain)NSString *sex;
@property (nonatomic ,assign)NSInteger age;
@property (nonatomic ,assign)CGFloat score;
- (void)dealloc;
@end
***************************
Person.m
#import "Person.h"
@implementation Person
//实现NSCopying里的方法
- (id)copyWithZone:(NSZone *)zone{
//开辟一份新的空间,与原对象所开辟的空间一样
//zone:原有对象的空间的大小
//第一步:创建一个新的对象
Person *per = [Person allocWithZone:zone];
//第二步:将原有对象的内容拷贝到新的空间上(也就是为新的对象赋值)
per.name = self.name;
per.age = self.age;
per.sex = self.sex;
per.score = self.score;
//将新对象返回即可
return per;
}
//销毁方法,当前该类的对象的引用计数为零时则会自动调用,用来回收对象的空间
- (void)dealloc{
NSLog(@"oh my god!,我被销毁了!");
//调用父类该方法,销毁对象内存,此时才是真正回收内存
[super dealloc];
}
@end