2

我正在学习 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

主.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
4

3 回答 3

5

它不取决于对象是否在函数中创建,而是取决于函数是否具有“保留的返回值”或“未保留的返回值”(参见Objective-C 自动引用计数)。

默认情况下,只有、、、和系列中的方法具有保留返回值,这意味着该函数返回一个 (+1) 保留对象。alloccopyinitmutableCopynew

所有其他函数都有一个未保留的返回值。稍微简化一下,你可以说这些函数返回一个自动释放的值(受 ARC 编译器的优化),以确保返回的值在调用函数中有效。

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

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

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

现在该函数返回一个保留值,当您通过设置删除强引用时,该值会立即释放checkerFuncBased = nil

在将基于 func 的检查器指针设置为 nil 之前
销毁:基于函数的检查器
将基于 func 的检查器指针设置为 nil 后
在将本地创建的检查器指针设置为 nil 之前
已销毁:本地创建的检查器
将本地创建的检查器指针设置为 nil 后
于 2013-05-04T22:17:06.317 回答
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 语句,所以它会被自动释放。

于 2013-05-04T22:29:04.233 回答
-1

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

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

于 2013-05-04T21:56:50.210 回答