一.内存分为五大区 :栈区,堆区,全局区,常量区,代码区
2.栈区特点:
- 系统分配,系统回收
- 存储在栈区的数据为临时变量,即定义在方法体中的变量,包括形参.
- 栈区的特点是 先进后出
- 人为地分配,人为地回收
- 人为动态分配的空间,都是在堆区中开辟的
- 标记删除
- 存储常量数据
- 数据不可更改
- 在编译期,就存入到全局区
- 只有等程序结束退出才释放
- 全局变量只能被初始化一次 如果没有赋初始值,默认初始值为0
二.内存管理
1.Manul Reference count & Auto Reference Count (MRC &ARC) referenceCount 引用计数 系统根据引用计数的个数来回收内存
要注意 引用计数 是为了管理内存 (1.防止出现野指针. 2.防止内存泄露)
2.几个内存管理的要用到的方法:+ alloc -retain -release -copy -autorelease
(1) +alloc (ReferenceCount)由0-1的过程
(2) -retain ( +1 返回地址 )
Person *person1=[person retain]; person 所指对象的引用计数加一
(3)-release (-1 无返回值 )
[person1 release]; person1所指对象的引用计数减一
(4)-copy (本身引用计数不变,副本引用计数加一 返回新地址)
Person *p2=[p copy];p 所指对象的引用计数不变 p2所指对象的引用计数加一
(5)-autorelease 在将来的某个时间将引用计数减一 注意 autorelease 之后 对系那个的 retainCount 不会马上减一 而是在将来某个时间减一 (这取决于他处于的 autoreleasepool什么时候释放).
3.delloc 是由父类继承过来的类,有系统自动调用,当 对象 的引用计数值为0时,系统会自动调用 delloc (自己可以重写个 delloc 方法用于判断对象是否销毁);
注意的一点是[super delloc]表示现在要调用从父类继承过来的 delloc 方法 (注意 :这一句话要放在方法体的最后边);
在 dealloc 方法中要将所有 有retain属性 的实例变量 都 release 然后要使用[super dealloc];
4.
autoreleasepool 的工作原理 就像一个栈,哪个对象先被放进 pool ,哪个对象就最后 release (栈中存的是对象的地址,即这一块要释放的内存的地址)
autoreleasepool 目前的建立方式是@ autoreleasepool{ }
autoreleasepool中可以嵌套多个 autoreleasepool{}以便释放在程序中多次出现的有 autorelease 方法 的对象,避免内存空间不足,造成泄露.
5.内存管理的原则:
(1).引用计数的增加和减少相等,当引用计数降为0之后,不应该再使用这块内存
(2).凡是使用(要理解为看到了,没有看到就不减)了 alloc,retain,或者 copy 让内存的引用计数增加了,就需要使用 release或者 autorelease 让内存的引用计数减少.在一段代码内,增加和减少的次数要相等.当引用计数值为0 时 ,对象所占的内存,就被系统回收
(3).在一个{ }中referenceCount 的增加的次数要和减少的次数一样.有两个原因:
- 1.是为了遵守内存管理原则.
- 2 有的必须要进行释放,因为比如在一个 if{ }的代码块中,如果没有 某些临时变量使用 alloc开辟了空间,那么出来 if{}代码块 这些临时变量就被系统销毁了,哪些创建的空间就会被 crash ,更为严重的时在 for 循环中开辟空间的时候.
6.copy 属性: 当一个类要使用 copy时, 生成自己的关于那个对象的副本 就一定要实现 NSCopying 协议 并且实现 NSCopying 协议中的 - copyWithZone:方法 .
并且在这个方法中自己实现 copy 的细节 一般式要创建一个新的对象,将 self 的值赋给这个对象 然后返回这个对象的地址
例:@interface Person:NSObject<NSCopying>
……..
@end
@implementation Person
- (id)copyWithZone:(NSZone *)zone{
Person *per=[[Person alloc]initWithName:self.name age:self.age gender:self.gender];
return per;
}
@end
person *p=[[Person alloc] initWithName:self.name age:self.age gender:self.gender];
Person *p2=[p copy];
copy
内部的
alloc
不在内部释放
不然在外面看到
copy
时
会又释放一次
注意:如果 没有实现 NSCopying 协议 而直接使用 copy 方法 就会引发crash
不是任何对象都可以接收 copy 消息只有接受了 NSCopying 协议的对象才能接收 copy 消息
7.
关于浅拷贝 与 深拷贝
浅拷贝 与 深拷贝 根据自己的理解 其实并没有一个完整的定论.
一般的理解是 浅拷贝-----只是实现了”指针" 的拷贝 (内容共用,一个对象中内容的改变 会 导致另一个对象中内容的改变)
深拷贝—---至少有一层对象实现了产生对象的副本 (内容不共用, 一个对象中内容的改变,不会影响另一个对象)
完全拷贝———每一层都实现了对象的副本的产生(类中每一个实例变量都实现了深拷贝(每一层对象都是))
其实 关于深拷贝 与浅拷贝 只是一个理解或者概念上的问题 不需要深究什么是深拷贝,什么是浅拷贝 他们只是几种拷贝方式
8.如果出现
两个对象相互引用的情况(如: husband 中存在 wife 对象 ,wife 中存在 husband 对象) 此时对象作为实例变量的属性的 attribute 的设置不应该两个对象同时都设置为 retain 或 copy, 否则会造成 retain Cycle ,导致两个对象都无法 dealloc . 解决办法是将一个类的实例变量设置为 assign 表示弱引用, 但这种方法要注意内存管理与野指针情况.
同时要注意
代理委托的情况,当出现代理的情况的时候,代理的语义属性要设置为 assign 防止代理与其委托之间的循环引入,而导致无法释放
最终一个设置语义属性的一个基本规则是: 属性的类型凡是没有带 * 都要设置为 assign (包括代理的 id 类型);
我们常见的delegate往往是assign方式的属性而不是retain方式 的属性,赋值不会增加引用计数,就是为了防止delegation两端产生不必要的循环引用。如果一个UITableViewController 对象a通过retain获取了UITableView对象b的所有权,这个UITableView对象b的delegate又是a, 如果这个delegate是retain方式的,那基本上就没有机会释放这两个对象了。自己在设计使用delegate
模式时,也要注意这点。