注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

又一个部落格

换过很多备忘录,不知道这本怎么样!

 
 
 

日志

 
 

cocoa objective-c 内存管理(待续)  

2010-04-23 00:50:19|  分类: cocoa |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
内存管理有一个非常重要的概念,就是对象的拥有者
两种情况下,你会成为一个对象的拥有者
1 使用了 包含 alloc new copy 关键字(例如,alloc,newObject,mutableCopy)方法或者发送了copy消息创建了对象.
2 你对一个对象发送了 retain 消息

如果我们是一个对象有拥有者,那么我们必须在不再使用它的时候释放他,使用release或者autorelease.

其实每个对象的背后都有一个 retain count(nsobject协议里就有retainCount方法,可以用这个方法查看这个计数器)
1。 当创建一个对象的时候 自动 +1
2。 当对一个对象发送了 retain消息, 自动+1
3。 当对一个对象发送了 release消息 自动-1
4。 当对一个对象发送 autorelease的时候, 一段时间后 -1
5。 当一个对象的 retain count 减少到0的时候. 对象被释放了

自动释放(autorelease)
(NSArray *)sprockets {
NSArray *array = [[NSArray alloc] initWithObjects:mainSprocket,
auxiliarySprocket, nil];
return [array autorelease];
}
自动释放往往运用在这种地方,因为你根本就不知道 array什么时候会被释放
如果我们改造一下上面的函数,用下面的方式
(NSArray *)sprockets {
NSArray *array = [[NSArray alloc] initWithObjects:mainSprocket,
auxiliarySprocket, nil];
return array;
}
虽然返回的array还是可以继续使用,但是,作为命名约定,函数名称中并没有迹象表明,接收返回的array需要负责释放,所以很容易造成内存泄露

下面是错误的写法
(NSArray *)sprockets {
NSArray *array = [[NSArray alloc] initWithObjects:mainSprocket,
auxiliarySprocket, nil];
[array release];
return array; // array is invalid here
}
返回了一个无效的array

再看这个写法
(NSArray *)sprockets {
NSArray *array = [NSArray arrayWithObjects:mainSprocket,
auxiliarySprocket, nil];
return array;
}
咋一看,似乎是错误的,但是其实他是正确的写法,因为arrayWithObjects方法返回的对象,你对他没有所有权,按照命名规则,方法名称中没有迹象表明他需要我们负责释放他,他自己会负责释放自己

总结:我们并不需要去关心一个对象的 retain count,也不需要关心返回的对象是否会自己释放自己.我们只关心我们对这个对象是否有所有权,有所有权,就必须负责去释放他.
你是否对一个对象有所有权只有两种情况,1 你创建(alloc new copy)了他. 2 你保留(retain)了他.

接下来了解一下共享对象的生存期
一般来说,我们接收到一个函数返回的对象,在当前的生存期内是有效的,甚至还可以继续返回给上一层,而不用担心他会被释放掉
但是这里有两个例外
1 接收到的对象是一个集合的元素,这个对象从集合中删除掉了
heisenObject = [array objectAtIndex:n];
[array removeObjectAtIndex:n];
// heisenObject could now be invalid.
2 接收到的对象是一个类的子对象,然后“父对象”被立即释放(不是 autorelease),并且这个子对象只有父对象这一个拥有者。
id parent = <#create a parent object#>;
// ...
heisenObject = [[parent child] ;
[parent release]; // Or, for example: self.parent = nil;
// heisenObject could now be invalid.

为了避免上诉两种情况,那么接收的时候,我们要进行一次保留操作,然后在不需要他的时候,释放掉他
heisenObject = [[array objectAtIndex:n] retain];
[array removeObjectAtIndex:n];
// use heisenObject.
[heisenObject release];

怎样正确的设置类的对象属性
我们要确保在使用类的对象属性的时候他是有效的,而且在释放类的时候,确保对象属性也会正确的被释放掉
(void)setMainSprocket:(Sprocket *)newSprocket {
//有些同学就觉得奇怪,当第一次运行这个函数的时候,mainSprocket=nil,我们发送了 autorelease,不会出错吗?
    //的确,发送给nil消息是允许的,cocoa是这么设计的
[mainSprocket autorelease];
mainSprocket = [newSprocket retain]; /* Claim the new Sprocket. */
return;
}
上面这样做,假如原先的newSprocket被修改了,那么mainSprocket同样也会被修改,如果想不被修改,可以进行一个copy
(void)setMainSprocket:(Sprocket *)newSprocket {
[mainSprocket autorelease];
mainSprocket = [newSprocket copy]; /* Make a private copy. */
return;
}


销毁一个对象
但一个对象被释放的时候,dealloc方法会被自动执行,这个方法负责释放对象内存,释放持有的资源,释放所有实例对象所有权

循环保留
这种情况出现在两个对象相互把对方保留
比如 document对象里有一个page对象 保留他(retain),page对象里为了知道自己属于哪个document所以也把document作为自己的一个属性,并保留他(retain)
那么当我们释放document的时候,释放不掉,因为还有page对象是document的拥有者,然后我们接着来释放这个page对象,发现也释放不掉,
因为这个page的另一个拥有者是我们刚才释放的document,这样就造成了永远释放不掉的循环
所以为了避免出现该问题,我们的原则是,父类可以retain子类,但是子类不能retain父类

弱引用
我们对一个对象发送一个retain,叫做强引用,只有所有的强引用都release了,对象才会释放,就像上面的例子,假如两个对象相互强引用,
就会造成永远释放不掉的恶心循环,所以这时候,我们需要一个弱引用.弱引用的意思就是,创建一个对象指针,但不保留。
弱引用还经常出现在 table data sources, outline view items, notification observers, and miscellaneous targets and delegates.
比如NSTableView 对象不保留他的data source,NSApplication对象不保留他的委托(delegate).
需要注意的是,弱引用是非常容易造成系统崩溃,所以,一般使用的时候,当一个对象被弱引用了,那么当自己销毁的时候应该发送
消息让那些引用你的对象知道你销毁了,把指针设置为nil,以避免造成系统崩溃. 这里就可以使用 notification center 来发送和接受消息
当一个委托对象销毁的时候,需要发送一个 setDelegate:nil 消息到委托你的对象去.

资源管理
很多资源,我们无法给他们设计dealloc,比如网络连接,缓存,缓冲 ==,因为他们很可能随时被释放.或者断开.


有一个地方会很容易造成内存泄露
很多时候,为了避免内存泄露,我们都会设置房问题,如下
- (void)setCount:(NSNumber *)newCount {
    [newCount retain];
    [count release];
    // make the new assignment
    count = newCount;
}
但是,有两处地方觉得不能使用访问器,那就是 init方法 和dealloc方法中
假如在init方法中使用, 刚创建的变量 retain count 初始值会是2,造成释放不掉,导致内存泄露.
在dealloc中当然也没有必要去给实例变量设值

有两种方法可以重设自己的 retain count
1:
- (void)reset {
    NSNumber *zero = [NSNumber numberWithInteger:0];
    [self setCount:zero];
}
2:
- (void)reset {
    NSNumber *zero = [[NSNumber alloc] initWithInteger:0];
    [self setCount:zero];
    [zero release];
}

容易造成混乱的地方
NSMutableArray *array;
NSUInteger i;
// ...
for (i = 0; i < 10; i++) {
    NSNumber *allocedNumber = [[NSNumber alloc] initWithInteger: i];
    [array addObject:allocedNumber];
    [allocedNumber release];
}
可能这里会不理解,为什么allocedNumber最后被释放掉了,因为, addObject 保留了一次..

待续......



以上的规则基本适用于 Core Foundation objects(核心基类)的内存管理规则,但是有特殊情况
特别是方法返回的对象 例如下面的代码 我们不负责释放
MyClass *myInstance = [MyClass createInstance];

//////////下面是 核心基础类的规则///  暂时还不知道是根据什么分 核心基础 和其他的,可能是比较底层,牵涉到对象是怎么产生的,和c语言相关////////
几个概念, 分配器,引用计数机制,对象所有权,自动释放池..


所有权:一个对象有一个或者多个拥有者,如果一个对象的retain count 等于0  那么这个对象被释放了
1,你创建了一个对象,那么你拥有他(copy的也算).
2,如果你接受到一个对象,并没有拥有它,他可能随时被释放掉,要想成为拥有者,必须使用 CFRetain
3,如果你是一个对象的拥有者,当适用完成后必须释放所有权,适用CFRetain

命名约定:
当一个函数名称里包含了 copy  create ,那么你有所有权
如果一个函数名称里包含了get ,那么你没有拥有权




Memory Management Programming Guide for Cocoa
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html#//apple_ref/doc/uid/10000011i

Memory Management of Core Foundation Objects in Cocoa
http://developer.apple.com/iphone/library/documentation/CoreFoundation/Conceptual/CFMemoryMgmt/CFMemoryMgmt.html#//apple_ref/doc/uid/10000127i

  评论这张
 
阅读(637)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017