IOS面经系列(一)浅析OC之内存管理
来源:互联网 发布:java怎么打成jar 编辑:程序博客网 时间:2024/04/30 20:17
要谈oc的内存管理,首先得从对象的生命周期说起。
1.对象的生命周期
1.1引用计数
oc利用引用计数来计算对象的使用寿命。每个对象都有一个与之相联的整数,称作它的引用计数器/保留计数器。
当某段代码需要访问一个对象时,该代码将该对象的保留计数器的值加1,表示为“我要访问该对象”。当这段代码结束对象访问时,将对象的保留计数器值减1,表示它不再访问该对象。当保留计数器值为0,表示不再有代码访问该对象了。因此对象将被销毁,释放内存被系统回收以便重用。
当使用alloc,new方法或者通过copy消息(生成对象的一个副本)创建一个对象时,对象的保留计数器值被置为1。要增加对象的保留计数器值,可以给对象发送retain消息。要减少则发release消息。
当一个对象因其保留计数器归0而即将被销毁时,oc自动向对象发送dealloc消息。你可以重写对象的dealloc方法,释放已经分配的相关资源。发送retainCount来查询引用计数。
下面举个栗子来更深入了解:
@interface RetainTracker : NSObject@end@implementation RetainTracker- (id) init{ if(self = [super init]){ NSLog(@"init:retainCount :%d",[self retainCount]); } return self;}- (void)dealloc{ NSLog(@"dealloc:bye"); [super dealloc];}@end
int main (int argc, const char * argv[]) { RetainTracker *tracker = [RetainTracker new]; //init:retainCount:1 [tracker retain]; //count:2 NSLog(@"%d",[tracker retainCount]); [tracker retain]; //count:3 NSLog(@"%d",[tracker retainCount]); [tracker release]; //count:2 NSLog(@"%d",[tracker retainCount]); [tracker release]; //count:1 NSLog(@"%d",[tracker retainCount]); [tracker retain]; //count:2 NSLog(@"%d",[tracker retainCount]); [tracker release]; //count:1 NSLog(@"%d",[tracker retainCount]); [tracker release]; //dealloc:bye return 0;}
1.2对象所有权
也许你觉得内存管理so easy。 但是我告诉你,当你开始思考对象所有权时,内存管理就变得复杂多了。
当我们说某个实体“拥有一个对象”时,就意味着该实体要负责确保对其拥有的对象进行清理。
如果一个对象具有指向其他对象的实例变量,则称该对象拥有这些对象。
举个栗子:实例对象拥有他的成员变量。同样,如果一个函数创建了一个对象,则该函数拥有它创建的这个对象。
当多个实体拥有某个特定对象时,对象的所有权关系就复杂了,这也是保留计数器值可能大于1的原因。在上面的代码例子里,main()函数拥有RetainTracker的对象,因此main()要负责清理该类的对象。
假如有Car类,其engine setter方法为:
-(void)setEngine:(Engine *)newEngine;
在main()里调用该方法:Engine *engine = [Engine new];[car setEngine : engine];
现在是哪个实体拥有engine对象呢?是main()函数 还是car类?哪个实体负责确保当engine对象不再被使用时能够收到release消息?因为car类正在使用engine对象,所以不可能是main函数,因为main函数随后可能还会使用engine对象,所以也不可能是car类。
解决办法是让car类保留engine对象,将engine对象的保留计数器增加到2,car类应该在setEngine方法中保留engine对象,而main函数运行结束时释放engine对象,当car类完成其任务时再释放engine对象(在其dealloc方法中),最后engine对象占用的资源被回收。
- (void) setEngine: (Enigne *) newEngine{ if(engine != newEngine) { [engine release]; engine = [newEngine retain]; }}//setEngine
最完美的解决方法是在访问方法中 先判断新旧对象是否相等,如果不是, 先释放旧对象,再保持新对象。
2.自动释放自动释放池(autorelease pool),它是一个存放实体的池(集合),这些实体可能是对象,能够被自动释放。NSObject类提供了一个autorelease方法。
该方法预先设定了一条在将来某个时间发送的release消息,其返回值是接收消息的对象。当给一个对象发送autorelease消息时,实际上是将该对象添加到NSAutoreleasePool中。当自动释放池被销毁时,会向该池中的所有对象发送release消息。
- (NSString *) description { NSString *description; description = [[NSString alloc] initWithFormat: @"I am %d years old", 4]; return ([description autorelease]);7} // description
自动释放池的销毁时间:
在使用Foundation库工具中,创建和销毁自动释放池的方法非常明确:
NSAutoreleasePool *pool;pool = [[NSAutoreleasePool alloc] init];…[pool release];
创建一个自动释放池时,该池自动成为活动的池。
释放该池时,其保留计数器值归0,然后该池被销毁。
在销毁过程中,该池释放其包含的所有对象。
当使用AppKit时,Cocoa定期自动为你创建和销毁自动释放池。通常是在程序处理完当前事件(如鼠标单击或按键)以后执行这些操作。
你可以使用任意多的自动释放对象,当不再使用它们时,自动释放池将自动为你清理这些对象。
@interface RetainTracker : NSObject @end @implementation RetainTracker - (id) init { if (self = [super init]) { NSLog(@"init: Retain count of %d.",[self retainCount]); } return self; } - (void) dealloc { NSLog(@"dealloc called.Bye Bye."); [super dealloc]; } @end int main (int argc, const char * argv[]) { // 创建一个自动释放池 NSAutoreleasePool *pool; pool = [[NSAutoreleasePool alloc] init]; // 创建(new)一个RetainTracket对象 RetainTracker *tracker = [RetainTracker new]; // count:1 NSLog(@"%d",[tracker retainCount]); // 处于演示目的,retain一个trancker [tracker retain]; // count:2 NSLog(@"%d",[tracker retainCount]); // 向对象发送autorelease消息,将该对象添加到自动释放池pool中 [tracker autorelease]; // count: still 2 NSLog(@"%d",[tracker retainCount]); // release该对象以抵消上面的 retain [tracker release]; //count: 1 NSLog(@"%d",[tracker retainCount]); NSLog(@"releaseing pool"); [pool release]; return 0; }
3.内存管理规则
1.当你使用new,alloc,copy创建一个对象时,该对象的保留计数器值为1.当不再使用该对象,你要负责向该对象发送一条release或autoRelease消息。这样,该对象将在其寿命结束时销毁。
2.当你通过任何方式获得一个对象时,则假设该对象的保留计数器值为1,而且已经被设置为自动释放,你不再需要执行任何操作来确保该对象被清理。如果你打算在一段时间内拥有该对象,则需要保留它并确保在操作完成时释放它。
3.如果你保留了某个对象,你需要(最终)释放或自动释放该对象。必须保持retain方法和release方法的使用次数相等。
4.垃圾回收(ARC)
oc2.0引入了自动内存管理机制,也称垃圾回收。
垃圾回收器会定期检查变量和对象以及他们之间的指针,当发现没有任何变量指向某个对象时,会将该对象视为垃圾。
最糟糕的事情莫过于保留一个指向不再使用的对象的指针。因此,如果你在一个实例变量中指向某个对象,一定要在某个时候将该实例变量置为nil,以取消对该对象的引用,并使垃圾回收器知道该对象可以被清理。
- IOS面经系列(一)浅析OC之内存管理
- iOS开发系列—Objective-C之内存管理oc
- OC之内存管理
- OC之内存管理
- OC之内存管理
- OC之内存管理
- OC之内存管理
- OC之内存管理
- OC之内存管理
- oc之内存管理
- IOS面试题之内存管理
- OC之内存管理(笔记)
- OC语言之内存管理
- OC学习心得之内存管理
- iOS之内存管理
- iOS之内存管理
- IOS之内存管理
- 黑马程序员-----oc语言学习笔记之内存管理一
- [Other] Inno Setup 使用心德-一个完整的项目 包括.NET组建捆绑 去空格等
- 职场人要远离的各种“负能量”
- FileZilla 帐号密码
- 判断数据库表是否存在,删除再创建
- Relevance Ranking: from Web Search to Vertical Search 报告
- IOS面经系列(一)浅析OC之内存管理
- 突破思维的障碍
- memcached启动过程2(改用自己的语言来叙述)
- Qt出现错误“undefined reference to `vtable for XXXXX.XXX”
- ADF VO知识
- Map 遍历取值及jstl的取值
- IOS:表视图
- opencv学习(2)添加椒盐噪声
- JSP向数据库中插入记录并返回记录id