笔记总结:
一、基本原理
1. 什么是内存管理
* 移动设备的内存极其有限,每个app所能占用的内存是有限制的
* 当app所占用的内存较多时,系统会发出内存警告,这时得回收一些不需要再使用的内存空间。比如回收一些不需要使用的对象、变量等
* 管理范围:任何继承了NSObject的对象,对其他基本数据类型(int、char、float、double、struct、enum等)无效
2. 对象的基本结构
* 每个OC对象都有自己的引用计数器,是一个整数,表示“对象被引用的次数”,即有多少人正在使用这个OC对象
* 每个OC对象内部专门有4个字节的存储空间来存储引用计数器
3. 引用计数器的作用
alloc、new或者copy创建一个新对象时,新对象的引用计数器默认就是1
* 当一个对象的引用计数器值为0时,对象占用的内存就会被系统回收。换句话说,如果对象的计数器不为0,那么在整个程序运行过程,它占用的内存就不可能被回收,除非整个程序已经退出
4. 引用计数器的操作
retain消息,可以使引用计数器值+1(retain方法返回对象本身)
* 给对象发送一条release消息,可以使引用计数器值-1
retainCount消息获得当前的引用计数器值
5. 对象的销毁
* 当一个对象的引用计数器值为0时,那么它将被销毁,其占用的内存被系统回收
* 当一个对象被销毁时,系统会自动向对象发送一条dealloc消息
* 一般会重写dealloc方法,在这里释放相关资源,dealloc就像对象的遗言
* 一旦重写了dealloc方法,就必须调用[super dealloc],并且放在最后面调用
* 不要直接调用dealloc方法
* 一旦对象被回收了,它占用的内存就不再可用,坚持使用会导致程序崩溃(野指针错误)
***笔记(基本理论)***
retain 返回对象本身 比如:
Person *p = [[Person alloc] init]; // retainCount为1,p是在栈内存中,其内保存着指向堆内存中开创出来的Person对象地址的值。
[p retain]; // 返回本身 亦可表示成 p = [p retain] // retainCount为2
[p release]; // 计数器减1 retainCount 为1
[p release]; // 计数器再减1retainCount 为0 ,将自动调用person对象的 dealloc方法,释放内存。注意,此时,p 中仍然保存着person对象的地址值,但此时,联系已经断开了,堆内存中的person对象已经消除了,称为僵尸对象!p此时也称为野指针!
p.age = 5; // 注意,此处等于调用僵尸对象赋值,可能会不报错。在Xcode中开启内存管理开关(僵尸对象检查机制),就会报错。打开方法:Editschema --> Diagnostics -->Objective-c Enable Zombie Objects 上打钩
[p release]; // 此时会报错。野指针指向的僵尸对象没法release;
p = nil; // 将 p 这个野指针清空,此时称为 空指针
[p release]; // 此处无错,空指针指向空对象可以release
二、set方法内存管理
如果你有个OC对象类型的成员变量,就必须管理这个成员变量的内存。比如有个Book *_book
1. set方法的实现
void)setBook:(Book *)book{
if
[_bookrelease];
_book= [book retain];
}
}
2. dealloc 方法的实现
void)dealloc {
[_book release];
super
}
三、@property参数
1. 控制set方法的内存管理
release旧值,retain新值(用于OC对象)
* assign : 直接赋值,不做任何内存管理(默认,用于非OC对象类型)
* copy : release旧值,copy新值(一般用于NSString *)
2. 控制需不需生成set方法
* readwrite :同时生成set方法和get方法(默认)
* readonly :只会生成get方法
3. 多线程管理
* nonatomic 非多线程 性能高
* atomic 多线程 性能低 注意,默认是atomic的
示例:
@property(nonatomic,retain) NSString * name;
@property(nonatomic,assign) int age;
四、循环引用问题
即:A类中有一个属性是B对象,B类中有一个属性是A对象。这种情况在使用过程中,是会报错的。解决方法如下:
在一个类中用assign声明属性,另一个类中用retain声明属性即可
额外说明:在.h文件中,用 @class 方式声明一个欲使用的类,在.m文件中再用#import引用类 (如有用到该类方法需导入此类时)
五、autorelease
* 给某个对象发送一条autorelease消息时,就会将这个对象加到一个自动释放池中
* 当自动释放池销毁时,会给池子里面的所有对象发送一条release消息
* 调用autorelease方法时并不会改变对象的计数器,并且会返回对象本身
* autorelease实际上只是把对release的调用延迟了,对于每一次autorelease,系统只是把该对象放入了当前的autoreleasepool中,当该pool被释放时,该pool中的所有对象会被调用Release
使用了autorelease,就不必再顾虑对象在何处release了,对象会被扔到自动释放池里,工作完了会自动release池子里的对象。autorelease返回对象本身。
注意:
1> 使用了autorelease,对象的引用计数并不会变化。
2> 占用内存较大的对象,不要随便使用autorelease,因为他会一直占用着内存,等待自动释放池销毁。反之,占用内存较小的对象,使用autorelease则没有多大影响。
3> autorelease错误写法1:
@autorelease{
Person *p = [[[Person alloc] init] autorelease]; // 计数器为 1
[p release]; // 计数器为0 此时p为野指针
} // 自动释放池在此行销毁,会再次release一次其内的对象,也即再一次[prelease],而此时p是野指针了,所以会报错
错误写法2:
@autorelease{
Person *p =[[[[Person alloc] init] autorelease]autorelease];
}// 连续多次调用autorelease等效于自动释放池一销毁,会多次调用release,会引发野指针调用错误。
autorelease实用技巧:
若每次创建对象,都如此这般 Person * p = [[[Person alloc] init] autorelease]; 会显得很繁琐,尤其是多次创建对象。此时,可以在类中建立一个静态方法,示例如下:
self alloc] init ] autorelease]; }
这样,每次创建对象的时候,只要 Person * p = [Person person];即可。
注意,这里用self而不是Person是有讲究的,原因是考虑到Person的子类情况。例如,GoodPerson继承 Person类,如果此处不用self,而用Person,那么GoodPerson *gp = [GoodPerson person];实际返回的还是Person对象,这样子类中的一些方法就无法使用了。
六、ARC
ARC的判断准则:只要没有强指针指向对象,就会释放对象。
指针分两种:
1> 强指针,默认情况下,所有指针都是强指针 __strong
2> 弱指针,__weak 使用示例:
__weak Person * p = [[Personalloc] init];
手动管理内存到ARC的等效转变
@propery(nonatomic,retain) ----->@property(nonatomic,strong) 适用于OC对象