Objective-C_内存管理
内存管理
MRC:
对象的生命周期:
1.通过alloc,new和copy等方式将对象产生出来,这个时期是对象的产生时期
2.初始化后的对象可以接收消息和执行动作,也可以和其它对象进行信息交换和相互调用,这个时期是对象的生存时期
3.对象完成其使命后,我们将其从内存中移除,这个时期是对象的死亡和销毁时期
引用计数器:
1.当对象刚刚被创建其初始值为1
2.当有某段代码需要访问一个对象时,会将该对象的引用计数器加1(通过retain来实现)
3.当某段代码结束访问一个对象时,会将该对象的引用计数器减1(通过release来实现)
4.当某段代码结束访问一个对象时,如果采用了autorelease方式,在当前自动释放池结尾会将该对象引用计数器减1
5.当对象的引用计数器为0时,代表所有其它代码均不再需要访问该对象,该对象会被自动销毁,其占用的内存空间会被回收
示例:
#import <Foundation/Foundation.h>
#import "Student.h"
#import "Dog.h"
int main(int argc, const char * argv[]) {
@autoreleasepool {
Dog *dog = [[Dog alloc]init];
NSLog(@"dog.retainCount:%ld",dog.retainCount);//为1
[dog release];
NSLog(@"dog.retainCount:%ld",dog.retainCount);
[dog bark];
//注意:写完之后多运行几次,看看会造成什么效果,dog虽然是release了,但是偶尔还能调用dog的方法,这是因为编译器有时候跑得很快,还没来得及释放,就已经跑完所有代码了,事实上dog已经释放了
}
return 0;
}
运行结果:
2015-09-17 16:39:37.633 OC_5.0_MemeryManage[853:43528] dog.retainCount:1
2015-09-17 16:39:37.635 OC_5.0_MemeryManage[853:43528] dog 已经被释放了
2015-09-17 16:39:37.635 OC_5.0_MemeryManage[853:43528] dog.retainCount:1
2015-09-17 16:39:37.635 OC_5.0_MemeryManage[853:43528] 汪汪汪汪。。。。
上面的例子,第二次retainCount,按原理应该为0,但却不是。苹果公司的文档关于此问题的解释是:在调试程序的时候观察对象的retainCount值是没有意义的,因为系统框架中的对象可能还会retain该对象以便保持对其引用,同时自动释放池也可能会持有该对象,因此不能仅凭retainCount来判断对象的引用计数。所以说retainCount只是一个参考值
对象所有权:
内存管理模型是基于对象所有权的。一个对象可能有一个或多个所有者。只要对象有走过一个所有者,则该对象会一直存在。
为了要搞清什么时候拥有对象,什么时候不再拥有,有如下的原则:
1.谁创建谁拥有,可以通过alloc,new,copy,mutableCopy等方法创建的对象
2.可以通过retain来获取对象的所有权
有两种情况需要使用retain:(1)在setter方法和init方法里,当你想要将一个对象作为属性来使用
(2)为了避免其它操作使对象变成无效的对象
3.如果不再需要一个对象,必须释放拥有对象的所有权
4.如果不拥有一个对象,不能释放该对象的所有权
集合与对象所有权
当我们将一个对象加入到集合(如数组、字典、set集合)中时,集合会拥有该对象的所有权;当集合移除该对象,或者集合对象本身被清理,集合会放弃对象的所有权。
为了确保内存管理不出错,总结了以下几点原则:
1.当使用new,alloc或copy方法创建一个对象时,该对象引用计数器为1.如果不需要使用该对象,可以使用release或autorelease方法,在其使用完毕将其销毁
2.如果通过其他方法获取一个对象,则可以假设这个对象,引用计数为1,并且被设置为autorelease,不需要对该对象进行清理,如果确实需要retain这个对象,则需要使用完毕后release
3.如果retain了某个对象,需要release或autorelease该对象,保持retain和release使用次数相等
总得来说就是,谁创建 ,谁释放。
在dealloc中释放对象的所有权
在使用dealloc中要注意:
1.绝对不要直接调用另一个对象的dealloc方法
2.在dealloc方法实现的最后要调用父类的dealloc方法
3.当应用结束时,系统未必会调用对象的dealloc方法
采用弱引用避免循环retain
例子:
教师类中有学生
#import <Foundation/Foundation.h>
@class Student;
@interface Teacher : NSObject
@property(nonatomic,retain)Student *student;
@end
学生类中有教师
#import <Foundation/Foundation.h>
@class Teacher;
@interface Student : NSObject
@property(nonatomic,retain)Teacher *teacher;
@end
相互retain强强引用,就会造成,你中有我,我中有你,要释放student,那么就要释放teacher,要释放teacher,那么就要释放student
大家都在等待对方释放,循环retain
解决办法就是其中一个使用弱引用assign;
ARC
ARC技术实际也是运用引用计数,只是程序员不用再去手动引用计数,相当于身边多了一个内存管理高手帮助我们去管理内存
ARC技术引入了一些新的规则:
1.不能显式调用,dealloc语句,或实现或者调用retain,release,retainCount和autorelease等方法
2.不能调用@selector(retain)、@selector(release)等等
3.可以实现dealloc,但方法中不用释放实例变量,也不能调用[super dealloc].
4.不能使用NSAutoreleasePool对象,用@autorelesepool块来代替
5.不能用开头为new的名称来定义一个属性,除非定义一个别的getter名称,如下:
//以下代码不能正常工作:
@property NSString *newTitle;
//这样可以
@property (getter = theNewTitle) NSString *newTitle;
浅拷贝与深拷贝
copy:
1.copy对于不可变对象相当于retain,
2.对于可变对象,是深拷贝(复制了一个新的内存空间,新对象与旧对象不是同一个)
3.但是深拷贝不一定是完全拷贝。对象里面复合的对象有可能还是浅拷贝,甚至是没有拷贝
mutableCopy:
不可变对象经过mutableCopy之后,发生了深拷贝,而且返回一个可变对象