手动引用计数下NSNumber等对象调用release后未释放的问题
问题分析与解决方案
你的问题根源出在setter方法的MRC实现错误,再加上NSNumber的特殊缓存机制干扰,下面一步步拆解清楚:
1. Setter方法的核心错误
先看你的setter代码:
- (void)setBar:(NSNumber*)value { [bar release]; bar = value; }
这段代码有两个致命问题:
- 没有retain新的value:在MRC规则里,如果你想让实例变量
bar持有传入的对象,必须调用retain提升它的引用计数。你现在只是把指针赋值过去,没有获取对象所有权,导致bar只是个“悬空”的指针,随时可能变成野指针。 - 没判断新旧值是否相同:如果不小心把同一个对象重复赋值给
bar,会先release旧值(也就是当前的bar),直接把对象引用计数打降到0,后续赋值的bar就成了无效指针。
2. NSNumber调用release后未被释放的原因
你发现NSNumber没被释放,这是系统的优化行为——NSNumber对小整数(通常是0-127范围)有全局缓存机制。系统会复用这些常用小整数的NSNumber实例,哪怕你调用release把引用计数降到0,系统也不会真正销毁它,而是留着供后续代码复用。这不是内存泄漏,是苹果特意做的性能优化,但你的setter错误会引发其他严重问题(比如野指针崩溃)。
3. 完整修复方案
修正Setter方法
改成标准的MRC setter写法:
- (void)setBar:(id)value { // 先判断新旧值是否一致,避免重复操作 if (bar != value) { [bar release]; // 释放旧值,放弃所有权 bar = [value retain]; // 持有新值,提升引用计数 } }
同时别忘了给Foo类加上dealloc方法,在对象销毁时释放bar,避免内存泄漏:
- (void)dealloc { [bar release]; [super dealloc]; // MRC中必须调用父类的dealloc方法 }
理解main函数的引用计数变化
你的main代码里,n通过alloc创建,引用计数为1;调用setBar后,bar通过retain把引用计数升到2;你调用[n release]后,引用计数回到1;当f被释放时,dealloc里的[bar release]会把引用计数降到0,这时候如果是普通对象(非缓存的NSNumber)就会被正确回收。
补充:MRC核心原则快速回顾
记住谁创建谁释放,谁持有谁释放:
- 通过
alloc/new/copy/mutableCopy创建的对象,你拥有所有权,必须调用release或autorelease。 - 调用
retain会获得对象所有权,之后必须调用release对应抵消。
内容的提问来源于stack exchange,提问作者Luke
相关产品推荐
相关产品推荐

