标签:
?、内存管理的?式
1、内存常见问题
(1)野指针异常:指针操作已经销毁的对象
指针指向某对象,该对象释放后,该指针即为野指针,对其操作造成野指针异常。
原因:过度释放。
(2)内存溢出:超出内存上限
iOS给每个应?程序提供了?定的内存,?于程序的运?。iPhone 3GS内存 30M左右,iPhone 5S 内存80M左右。?旦超出内存上限,程序就会Crash。
2、内存管理的方式
(1)垃圾回收(gc) | OC支持 — OS X开发 支持 | iOS 不支持
程序员只需要开辟内存空间,不需要?代码显式地释放,系统来判断哪些空间不再被使?,并回收这些内存空间,以便再 次分配。整个回收的过程不需要写任何代码,由系统?动完成垃圾回 收。Java开发中?直使?的就是垃圾回收技术。
(2)MRC (Manual Reference Count) | ??引?计数 | iOS支持
手动操作引用计数,手动调用控制引用计数的方法
内存的开辟和释放都由程序代码进?控制。
(3)ARC (Auto Reference Count) | ?动引?计数 | iOS支持
自动操作引用计数,编译器调用引用计数的方法
iOS 5.0的编译器特性,它 允许?户只开辟空间,不?去释放空间。它不是垃圾回收!它的本质 还是MRC,只是编译器帮程序员默认加了释放的代码。
?、内存管理机制
C语?中:使?malloc和free,进?堆内存的创建和释放。堆内存只 有正在使?和销毁两种状态。 实际开发中,可能会遇到,两个以上的指针使?同?块内存。C语? ?法记录内存使?者的个数。
OC采?引?计数机制管理内存,当?个新的引?指向对象时,引? 计数器就递增,当去掉?个引?时,引?计数就递减。当引?计数到 零时,该对象就将释放占有的资源。
1、引?计数
(1)引用计数 标记程序运行期间,对象被引用的次数
(2)通过操作引用计数,控制对象是否被销毁。
(3)当引用计数应该减为0时,对象自动被销毁,存储空间被回收
2、操作引?计数的?法
alloc\ retain\ copy\ release\ autorelease
(1)造成引用计数增加
除了NSString的对象使用copy 其余的对象类型都声明成retain
+alloc 当前对象 计数 0 -> 1
Person * p = [[Person alloc] init];
NSLog(@"%lu", p.retainCount);
-retain 当前对象 计数 加1
指针的引用
新对象与原对象指向相同对象
[pretain];
NSLog(@"%lu", p.retainCount);
Person * p3 = [p2 retain];
NSLog(@"%lu,%lu,%lu", p.retainCount, p2.retainCount, p3.retainCount);
-copy 原来的对象计数 不变 新的对象 0 -> 1
拷贝
详情见四
(2)造成引用计数减少
release 当前对象 立即减1
// 调用release,实现引用计数-1
[p release];
NSLog(@"%lu", p.retainCount);
[p release];
NSLog(@"%lu", p.retainCount);
// 当引用计数应该减为0时,对象被销毁,存储空间被回收(系统完成)
// person对象被销毁(只是标记删除,并不清空数据),不建议通过指针操作
// 对象被销毁后,打印引用计数显示结果为1
[p release];
NSLog(@"%lu", p.retainCount); // 应该减为0
autorelease 当前对象 延迟减1 非立即
[pe1 autorelease]; // 未来的某个时刻引用计数-1(管理其的自动释放池release时)
autoreleasepool的使?:
第一种写法:将对象写在pool的创建和release之间
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
Person * p = [[Person alloc] init]; // 1
[p retain]; // 2
[p retain]; // 3
NSLog(@"%lu", pool.retainCount);
[p autorelease]; // 3
[p autorelease]; // 3
NSLog(@"%lu", pool.retainCount);
// release销毁自动释放池(扔掉垃圾桶)
[pool release];
// drain清空自动释放池(只倒垃圾)
//[pool drain];
第二种写法:mrc支持两种,arc只支持第二种
Person * pe = [[Person alloc] init]; //1
[pe retain]; // 2
NSLog(@"--- %lu", pe.retainCount);
@autoreleasepool {
[pe autorelease]; // 2
NSLog(@"--- %lu", pe.retainCount);
}
NSLog(@"--- %lu", pe.retainCount); // 1
3、销毁对象
dealloc 引用计数将要减为0时,对象自动调用
(1)继承自NSObject,可以不实现,编译器默认实现
(2)如果实现dealloc方法
- (void)dealloc
{
代码
[super dealloc];
}
QA:
dealloc和release到底执行了什么操作?
三、内存管理的基本原则
引?计数的增加和减少相等,当引?计数降为0之后,不应该再使?这块内存空间。
正常情况
凡是使?了alloc、retain或者copy让内存的引?计数增加了,就需 要使?release或者autorelease让内存的引?计数减少。在?段代码 内,增加和减少的次数要相等。
异常情况:
假设当前pe指向的对象的引用计数为1
如果不写[pe release]; main函数运行结束后,局部变量pe被销毁,没有指针指向对象,造成内存泄漏
如果写了不止一句[pe release];造成过度释放。本质:野指针异常(pe 操作已释放的对象)。
四、掌握copy的实现
自定义对象的拷贝要实现NSCopying协议和NSMutablecopying协议,但是在NSCopying协议中也能实现本质上的深拷贝所以只需实现一个方法就可以。
1、深浅拷贝
深拷贝 拷贝的是对象,即创建了新的对象
浅拷贝 拷贝的是指针,即操作的是原来的对象,没有创建新的对象
// 实现浅拷贝
- (id)copyWithZone:(NSZone *)zone
{
return [self retain];
}
// 深拷贝
- (id)copyWithZone:(NSZone *)zone
{
// Person * p = [[Person allocWithZone:zone] init]; // 以前的iOS版本需要这样写,如果需要向下兼容可以写这句代码
Person * p = [[Person alloc] init];
p.name = self.name; // self表示调用copy方法的对象。即被拷贝的对象
return p;
}
2、copy 和 mutableCopy
对象调用copy方法得到的是不可变的对象
对象调用mutableCopy得到的是可变的对象
不可变对象创建不可变对象为浅copy其余全为深copy
Xcode打开僵尸对象:
NSString * str1 = @"易荟云";请问str1的retainCount是多少?
@""在常量区,str1的retainCount为最大值。
drain与release区别
在MRC下,两者基本一样,在GC环境下,release 是一个no-op(无效操 作),所以无论是不是gc都使用drain
iOS有没有垃圾回收?autorelease 和垃圾回收制(gc)有什么关系?
没有。autorelease只是延迟释放,gc是每隔一段时间询问程序,看是否有无指针指向的对象,若有,就将它回收。他们两者没有什么关系。
标签:
原文地址:http://my.oschina.net/zooyf/blog/490971