Objective-C 指针设置为 nil 时的引用计数(无 ARC)

Objective-C reference count when pointer set to nil (without ARC)

我想了解引用计数是如何工作的,所以我禁用了 ARC 并写了一个简单的 class:(Foo.h 未粘贴,因为它未修改)

Foo.m

@implementation Foo

- (instancetype)init
{
    NSLog(@"Init object");
    return [super init];
}

- (void)dealloc
{
    NSLog(@"Dealloc object");
    [super dealloc];
}

@end

Main.m

#import <Foundation/Foundation.h>
#import "Foo.h"

int main(int argc, const char * argv[]) {

    Foo *obj = [[Foo alloc] init];

    obj = nil;
    return 0;
}

现在我希望看到 dealloc object 日志,因为对 Foo 对象的唯一引用已经消失,但我得到的唯一消息是 init object.

为什么我看不到?我赋值obj = nil的时候对象不是释放了吗?

没有。如果您不使用 ARC,则该对象会在您调用 [obj release]; 时释放。 (ARC 为您插入这些调用。)将 obj 设置为 nil 在内存管理方面没有任何作用(尽管它确实创建了一个您无法再访问的对象!)。

基本上,在没有 ARC 的 Cocoa 中:

  • 如果您想获得某个对象的所有权,请致电 [obj retain]。 (alloc 为您完成。)
  • 当您想要放弃对象的所有权时,您可以调用 [obj release]。当对象的保留计数达到 0 时,release 依次调用 dealloc
  • 当您想要放弃当前范围之外的对象的所有权时,您可以调用 [obj autorelease]。最常见的情况是,当您 return 来自方法的对象(并且不想保留它的所有权)时会发生这种情况。

一些额外的观察来扩展 mipadi 的出色回答:

  1. 在非 ARC 代码中,将变量设置为 nil 不会 释放对象。您必须明确地 release/autorelease 它。

    更准确地说,如果所有权已转移给您(即您通过名称以 allocnewcopy 开头的方法获取对象) , 或 mutableCopy), 那么你有责任显式调用 release/autorelease.

    最重要的是,简单地将变量设置为 nil 是不够的。

  2. 我不敢提这个,怕混淆了问题,但在处理属性时,它有点不同。使用属性,调用 retain 属性 的 setter 将自动为您 retain 对象。如果您稍后将 属性 设置为 nil,那么 setter 将自动 release 为您设置。

    想象一个 class 像这样:

    @interface Bar : NSObject
    @property (nonatomic, retain) Foo *foo;
    @end
    

    当你想设置 foo 属性 时,你可以执行以下操作

    Foo *f = [[Foo alloc] init];  // create Foo object with +1 retain count
    self.foo = f;                 // the `retain` property will increase retain count to +2
    [f release];                  // resolve the local strong reference, reducing retain count back to +1
    

    或者,更简单地说:

    self.foo = [[[Foo alloc] init] autorelease];
    

    它们所做的是创建一个具有 +1 保留计数的 Foo 对象。通过设置 foo 属性,您正在创建另一个 strong 引用,因此最终保留计数为 +2。然后当你 release/autorelease 它时,保留计数回落到 +1.

    稍后,当您完成 foo 并想要释放它时,您只需再次调用 setter,这次使用 nil 值:

    self.foo = nil;              // resolve the `retain` done by the property, reducing the retain count to +0
    

    这解除了 foo 属性 对对象维护的强引用。您 自己打电话给release(至少在属性 上)。显然,当你用 nil 值调用 setter 时,不仅释放了先前的对象,而且如果那是最后一个强引用,该对象也会为你释放。

简而言之,原来的alloc偏移了一个release/autorelease,而不是设置变量为nil。但是,retain 属性 的设置是 通过将 属性 设置为 nil.

来解决的

一些最后的观察。

  • 我建议如果要编写非 ARC 代码,请仔细阅读 高级内存管理编程指南, 尤其是 Memory Management Policy 章节。

  • Xcode的静态分析器(shift+command+B ,或 Xcode 的 "Product" 菜单上的 "Analyze")非常擅长识别困扰非 ARC 代码的内存问题。始终确保您绝对没有来自静态分析器的警告。

  • 使用 Instruments 时,分配工具有一个名为 "Record Reference Counts" 的功能(参见 )。如果您发现某个对象没有被释放,此工具可用于诊断对象的完整生命周期及其所有保留计数。