为什么Objective-C ARC释放依赖于对象是否在函数中创建?

时间:2013-05-04 21:46:08

标签: objective-c automatic-ref-counting

我正在学习ARC内存管理,并遇到了对我没有意义的事情。

在下面的示例代码中,在main()中本地分配的对象在其指针被指定为nil时会被释放,正如我所期望的那样。

但是如果在另一个函数中分配了相同类型的对象,并且在main()中定义了指向它的指针,并且该指针设置为nil,则在main函数退出之前不会释放该对象。这对我来说很神秘。

在下面的代码中,创建了两个类GRMemoryChecker实例。在一种情况下,它直接在main()中分配,而在另一种情况下,main()调用itemMakerFunc()来进行分配。运行main()时,日志输出显示当函数指针设置为nil时,函数中分配的实例不会被释放 - 当函数退出时,它将被释放。

我认为这意味着itemMakerFunc()创建的实例在指针设置为nil之前有2个所有者,而本地创建的实例只有1个。

但为什么呢?当指针设置为nil时,还存在哪个其他所有者?或者如果没有其他所有者仍然存在,为什么计数器在它不存在时会减少呢?

GRMemoryChecker.h:

#import <Foundation/Foundation.h>

@interface GRMemoryChecker : NSObject
{
    NSString *name;
}
- (id)initWithName:(NSString *)str;

- (void)setName:(NSString *)str;

- (void) dealloc;
@end

GRMemoryChecker.m:

#import "GRMemoryChecker.h"

@implementation GRMemoryChecker

- (id)initWithName:(NSString *)str
{
    self = [super init];
    if (self)
    {
        [self setName:str];
    }
    return self;
}

- (void)setName:(NSString *)str
{
    name = str;
}

- (NSString *)description
{
    return name;
}

- (void) dealloc;
{
    NSLog(@"Destroyed: %@", self);
}
@end

的main.m:

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

GRMemoryChecker *itemMakerFunc()
{
    return [[GRMemoryChecker alloc] initWithName:@"func-based checker" ];
}

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        GRMemoryChecker *checkerLocallyCreated = [[GRMemoryChecker alloc] initWithName:@"locally-created checker"];
        GRMemoryChecker *checkerFuncBased = itemMakerFunc();

        NSLog(@"Before setting func-based checker pointer to nil");
        checkerFuncBased = nil;
        NSLog(@"After setting func-based checker pointer to nil");

        NSLog(@"Before setting locally-created checker pointer to nil");
        checkerLocallyCreated = nil;
        NSLog(@"After setting locally-created checker pointer to nil");
    }
    return 0;
}

控制台输出:

Before setting func-based checker pointer to nil
After setting func-based checker pointer to nil
Before setting locally-created checker pointer to nil
Destroyed: locally-created checker
After setting locally-created checker pointer to nil
Destroyed: func-based checker

3 个答案:

答案 0 :(得分:4)

它不依赖于对象是否在函数中创建,而是依赖于 函数具有&#34;保留的返回值&#34;或者“未获得的返回值”#34; (见Objective-C Automatic Reference Counting)。

默认情况下,alloccopyinitmutableCopynew个系列中仅包含方法 保留了返回值,这意味着该函数返回一个(+1)保留对象。

所有其他功能都有一个未保留的返回值。你可以说,简化一些事情 这些函数返回一个自动释放的值(取决于优化 由ARC编译器制作)以确保返回值在调用函数中有效。

当前自动释放池被销毁时,将释放自动释放的值。

您可以使用NS_RETURNS_RETAINED属性更改函数的行为:

GRMemoryChecker *itemMakerFunc() NS_RETURNS_RETAINED;
GRMemoryChecker *itemMakerFunc()
{
    return [[GRMemoryChecker alloc] initWithName:@"func-based checker" ];
}

现在该函数返回一个保留值,该值已取消分配 通过设置checkerFuncBased = nil

删除强引用后立即显示
Before setting func-based checker pointer to nil
Destroyed: func-based checker
After setting func-based checker pointer to nil
Before setting locally-created checker pointer to nil
Destroyed: locally-created checker
After setting locally-created checker pointer to nil

答案 1 :(得分:3)

当您手动保留和释放对象时,ARC会强制执行您应遵循的相同内存管理语义。那么让我们看看我们如何在MRR下编写这个,看看它是否能告诉我们什么:

GRMemoryChecker *itemMakerFunc()
{
    return [[[GRMemoryChecker alloc] initWithName:@"func-based checker" ] autorelease];
}

int main(int argc, const char * argv[])
{
    @autoreleasepool {
        GRMemoryChecker *checkerLocallyCreated = [[GRMemoryChecker alloc] initWithName:@"locally-created checker"];
        GRMemoryChecker *checkerFuncBased = itemMakerFunc();

        NSLog(@"Before setting func-based checker pointer to nil");
        checkerFuncBased = nil;
        NSLog(@"After setting func-based checker pointer to nil");

        NSLog(@"Before setting locally-created checker pointer to nil");
        [checkerLocallyCreated release];
        checkerLocallyCreated = nil;
        NSLog(@"After setting locally-created checker pointer to nil");
    }
    return 0;
}

基本上,alloc返回的引用是一个拥有引用,需要释放。当一个对象只存在于一个函数中时,我们可以直接释放它,它将立即被释放。但是当我们从函数返回它时,我们不能简单地释放它,因为它需要经过return语句,因此它会被自动释放。

答案 2 :(得分:0)

首先,你的dealloc方法正在泄漏。你必须在其中调用[super dealloc],否则永远不会释放对象。

关于两个实例的不同行为,当您在itemMakerFunc中返回已分配的对象时,ARC将在返回时应用“autorelease”。因此,该实例是自动释放的,但在其他固定器发布时不会立即释放。另一方面,保留另一个实例,然后在将nil赋值给变量时立即释放。认为autorelease是一个后置动作。它将在当前循环结束时释放,但不会立即释放。