Objective-C中retainCount返回错误值问题(MRC环境代码示例)
先把你的代码贴出来方便大家对照分析:
@class Person; @protocol PersonDelegate <NSObject> - (void)sendLetterForPerson:(Person *)p; @end @interface Person : NSObject @property (nonatomic, weak) id <PersonDelegate> delegate; - (void)writeLetterComplete; @end @interface TestController ()<PersonDelegate> @property (nonatomic, strong) Person *per; @end // 调用代码 _per = [[Person alloc] init]; _per.delegate = self; NSLog(@"%lu",(unsigned long)_per.retainCount); NSLog(@"%lu",(unsigned long)_per.delegate.retainCount);
好,咱们来拆解这个问题的核心:
问题根源
MRC不支持
weak关键字weak是ARC专属的内存管理修饰符,在MRC环境下,编译器要么把weak当作assign处理,要么直接抛出编译警告/错误。你用weak修饰代理,本质上和assign没区别,但这会让你对代理的内存管理逻辑产生误解——你以为weak会自动处理引用计数,但MRC里根本没有这个机制。retainCount本身就是个不可靠的“坑”
Apple官方早就明确说过,别依赖retainCount的返回值判断对象引用计数。系统内部会对对象做很多隐式操作:比如autorelease池的临时retain、NSString常量池缓存、调试环境的额外引用等,都会让retainCount的结果和你预期完全不符。比如你的TestController实例(也就是self)因为被视图层级持有,它的retainCount可能远大于你手动操作的数值。代理内存管理的认知偏差
MRC中代理的正确写法是用assign(用retain会导致循环引用),assign不会增加被代理对象的引用计数,所以你打印_per.delegate.retainCount得到的是self的引用计数,但这个值受系统影响极大,完全不能反映你设置代理的操作。
解决方案
把代理的
weak改成assign
这是MRC下代理的标准写法,既避免循环引用,又符合MRC内存管理规则:@property (nonatomic, assign) id <PersonDelegate> delegate;放弃用
retainCount验证内存管理
要确认对象是否会被正确释放,重写dealloc方法才是靠谱的方式。比如在Person和TestController中分别添加:// Person.m - (void)dealloc { NSLog(@"Person被释放了"); [super dealloc]; // MRC下必须调用super的dealloc } // TestController.m - (void)dealloc { NSLog(@"TestController被释放了"); [_per release]; // MRC下手动释放strong属性的对象 [super dealloc]; }运行后观察控制台的释放日志,这才是判断内存管理是否正确的标准。
确保MRC环境配置正确
检查target的Build Settings,把Objective-C Automatic Reference Counting设置为NO,避免ARC和MRC混合导致的奇怪问题。
内容的提问来源于stack exchange,提问作者user9067376

