You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

手动引用计数下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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.22 09:05:38