1、内存管理原因:
(1)内存溢出 内存不够用
(2)野指针异常 指针操作了不属于自己的存储空间,指针操作已经销毁的对象
2、内存管理的方式
(1)垃圾回收 OC支持,mac开发支持,iOS开发不支持
(2)mrc iOS开发 手动操作引用计数,手动调用控制引用计数的方法
(3)arc iOS开发 自动操作引用计数,编译器调用引用计数的方法
3、内存管理的机制
(1)引用计数 标记程序运行期间,对象被引用的次数
(2)通过操作引用计数,控制对象是否被销毁。
(3)当引用计数应该减为0时,对象自动被销毁,存储空间被回收
4、操作引用计数的方法
(1)造成引用计数增加
alloc 当前对象 0 -> 1
retain 当前对象 加1
copy 原来的对象 不变 新的对象 0 -> 1
(2)造成引用计数减少
release 当前对象 立即减1
autorelease 当前对象 延迟减1 非立即
5、销毁对象
dealloc 引用计数将要减为0时,对象自动调用
(1)继承自NSObject,可以不实现,编译器默认实现
(2)如果实现dealloc方法
- (void)dealloc
//当对象的引用计数应该减为0时,对象自动调用dealloc方法,销毁对象,释放内存空间
{
NSLog(@"Person对象被销毁");
[super dealloc];//这句代码写在dealloc方法实现的最后一步
}
引用计数
Person * p = [[Person alloc]init];
NSLog(@"%lu",p.retainCount );
[p retain];
NSLog(@"%lu",p.retainCount);
//指针p2指向Person对象,此时引用计数没有任何变化
Person * p2 = p;
//p2指向的Person对象,引用计数加1
//指针p3指向p,p2 指向的Person对象
Person * p3 = [p2 retain];
//打印引用计数
NSLog(@"%lu",p.retainCount);
NSLog(@"%lu",p2.retainCount);
NSLog(@"%lu",p3.retainCount);
//调用release,实现引用计数减1
[p release];
NSLog(@"%lu",p.retainCount);
[p release];
NSLog(@"%lu",p.retainCount);
//当引用计数应该减为0时,对象被销毁,存储空间被回收
//对象被销毁后,打印引用计数显示结果为1
[p release];
NSLog(@"%lu",p.retainCount);//应该减为0
//retainCount的作用:观察引用计数的变化,增加减少的变化
//不能使用上面创建的Person对象,已经被销毁
Person * pe1 = [[Person alloc]init];//1
[pe1 retain];//2
[pe1 autorelease];//未来某个时刻引用计数减1
NSLog(@"%lu",pe1 .retainCount);//2
autorelease
1、创建自动释放池对象
2、在自动释放池内,对 对象进行autorelease操作。
3、自动释放池会记录对象(如果同一个对象,两次调用autorelease,该对象会被记录两次)
4、当自动释放池销毁时,记录的所有对象,依次调用release方法
1)第一种写法
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
Person *p = [[Person alloc ] init ];//1
p.name = @"yihuiyun";
[p retain];//2
[p retain];//3
[ p autorelease];//3
NSLog(@"%lu",p.retainCount);
[ p autorelease];//3
NSLog(@"%lu",p.retainCount);
//销毁自动释放池
[pool release];//销毁
// [pool drain];清空
NSLog(@"%lu",p.retainCount);
2)第二种写法
Person *pe = [[Person alloc]init];
[pe retain];
NSLog(@"--%lu",pe.retainCount);
@autoreleasepool{
[pe autorelease];
NSLog(@"--%lu",pe.retainCount);
}
NSLog(@"--%lu",pe.retainCount);
//pe指向的对象的引用计数为1
//在这个位置:
//如果只写一句[pe release];对象正常被销毁,不存在内存问题
//如果不写[pe release];main函数运行结束后,局部变量pe被销毁,没有指针指向对象,造成内存泄露
//如果写了不止一句[pe release]; 过度释放,本质野指针异常
mrc支持两种写法,ARC只支持第二种
//深拷贝、浅拷贝
//深拷贝:拷贝的是对象,创建了新的对象
//浅拷贝:拷贝的是指针,操作的是原来的对象,没有创建新的对象
//Person类实现的深拷贝
Person *p1 = [[Person alloc ]init];
p1.name = @"yihui";
Person * p2 = [p1 copy];
NSLog(@"%@",p2.name);
NSLog(@"%@ %@",p1,p2);
NSLog(@"%lu %lu",p1.retainCount,p2.retainCount);
//Studnet类的浅拷贝
Student *s1 = [[Student alloc]init];
s1 .name = @"wu";
Person * s2 = [s1 copy];
NSLog(@"%@",s2.name);
NSLog(@"%@ %@",s1,s2);
NSLog(@"%lu %lu",s1.retainCount,s2.retainCount);
//字符串的retainCount
//常量字符串对象,retaincount是最大值
NSString *str1 = @"yihuiyun";
NSLog(@"str1 %lu",str1.retainCount);//1844...
// NSLog(@"str1 %d",str1.retainCount);//结果为-1(常量区)
NSString * str2 = [[NSString alloc]initWithFormat:@"yihuiyun"];
NSLog(@"str2 %lu",str2.retainCount );//打印结果1(堆区)
NSString *str3 = [[NSString alloc]initWithFormat:@"123"];
NSLog(@"str3 %lu",str3.retainCount);
//字符串类的initWithFormat方法创建的对象,可以在堆区或常量区
//内存管理只参考内存管理原则,即影响引用计数的5个方法
//NSString类型的对象,调用copy得到的结果
NSString * str4 = [[NSString alloc]initWithFormat:@"你大舅你二舅都是你舅"];
NSString * str5 = [str4 copy];
NSLog(@"%p,%p",str4,str5);
NSLog(@"%lu",str5.retainCount);
[str4 release];
[str5 release];
//copy 和MutableCopy
//对象调用copy方法,得到的是不可变的对象
//对象调用MutableCopy,得到的是可变的对象
NSArray *array1 = @[@"1",@"2",@"3"];
NSMutableArray * array2 = [array1 mutableCopy];
[array2 addObject:@"4"];
NSLog(@"array2 = %@",array2);
//copy方法的实现
- (id)copyWithZone:(NSZone *)zone
{
// Person * p = [[Person allocWithZone:zone]init];以前的iOS版本需要这样写,如果需要向下兼容,可以写这句代码
Person * p = [[Person alloc]init];
p.name = self.name;//self表示调用copy方法的对象,即被拷贝的对象
p.age = self.age;
return p;
}
//便利构造器内部实现了创建对象,使用autorelease造成对象被销毁
//使用便利构造器创建的对象,不能再使用release
//便利构造器创建的对象,被销毁的时间未知(在一段代码内,如果控制便利//构造器创建的对象的自动释放池未销毁,则对象暂时可以安全使用)
Person * p1 = [Person personWithName:@"wu"];