2

我偶然发现了一个在其他地方找不到答案的问题。当我调用一个方法,该方法返回指向稍后使用的对象的指针并最终设置为 nil 时,它仍然分配在内存中(根据 Instruments)。我正在使用 XCode 4.6.3 和 iOS 6.1。ARC 已打开。

这是示例代码:

类A.h

@interface ClassA : NSObject
    -(void)runSomething;
@end

类A.m

#import "ClassA.h"
#import "ClassB.h"

@implementation ClassA

-(void)runSomething {
    int counter = 0;
    while (true) {
        ClassB *instance = [self makeClassBWithNumber:counter];
        NSLog(@"%d", [instance getNumber]);
        [NSThread sleepForTimeInterval:0.01];
        instance = nil;
        counter++;
    }
}

-(ClassB*) makeClassBWithNumber:(int)number {
    return [[ClassB alloc] initWithNumber:number];
}

@end

B类.h

@interface ClassB : NSObject
@property int number;
    -(id)initWithNumber:(int)number;
    -(int)getNumber;
@end

B类.m

#import "ClassB.h"

@implementation ClassB

-(id)initWithNumber:(int)number {
    self = [super init];
    if(self) {
        _number = number;
    }
    return self;
}

-(int)getNumber {
    return [self number];
}

@end

ClassB 在视图控制器中创建并调用方法 runSomething。此示例代码生成创建的对象 (ClassB) 永远不会从内存中释放。如果我从

ClassB *instance = [self makeClassBWithNumber:counter];

ClassB *instance = [[ClassB alloc] initWithNumber:counter];

创建的对象在每个循环周期中被正确释放。这种行为的原因是什么?我在 stackoverflow 上找到了一些旧答案,这些答案makeClassBWithNumber应该返回调用 autorelease 的结果return [result autorelease],但是如果启用了 ARC,则无法做到这一点。

4

4 回答 4

5

makeClassBWithNumber返回一个自动释放的对象,即使使用 ARC。(更准确地说,它可以返回一个自动释放的对象,具体取决于优化。)

与手动引用计数的不同之处在于 ARC 编译器在需要时插入 autorelease 调用,而不是您。

来自Clang/ARC文档:

3.2.3 未保留的返回值

返回可保留对象类型但不返回保留值的方法或函数必须确保对象在返回边界上仍然有效。

当从这样的函数或方法返回时,ARC 在返回语句的求值点保留值,然后离开所有局部范围,然后平衡保留,同时确保值跨越调用边界。 在最坏的情况下,这可能涉及 autorelease,但调用者不得假设该值实际上在 autorelease 池中。

makeClassBWithNumber不是 alloc、copy、init、mutableCopy 或 new 方法,因此返回未保留的返回值。

于 2013-09-10T19:46:58.257 回答
5
于 2013-09-10T19:47:12.617 回答
1

正如其他人所说,您看到的区别在于方法返回和调用者拥有的对象还是调用者不拥有的对象。

前一类是alloc, init, new, copy&mutableCopy类中的方法。这些都返回调用者拥有的对象,ARC 将确保它被释放。

后一类中的所有方法不在第一类中!这些返回一个不属于调用者的对象,ARC 将确保在需要时保留该对象,如果保留则释放。此类别中的返回值可能在自动释放池中,这意味着它们将至少与它们在池中一样长(如果它们已被 ARC 保留,则可能更长)并且标准池在最后清空每个运行循环周期。这意味着在自动释放池中生成大量条目的循环中,许多不再需要的对象可以在池中累积 - 这就是您所看到的。

在 MRC 下,解决方案是在此类循环中引入本地自动释放池,以避免积累不再需要的对象。然而,在 ARC 下,这可能不是最佳选择。

在 ARC 下,更好的解决方案可能是遵循命名约定,在这种情况下,您要使用new模式 -是一种方法中+new的标准模式。所以将你的重命名为:allocinitmakeClassBWithNumbernewClassBWithNumber

// *create* a new ClassB object
- (ClassB *) newClassBWithNumber:(int)number
{
   return [[ClassB alloc] initWithNumber:number];
}

这表明该方法返回调用者拥有的一个对象,它是一个“创建”方法,ARC 将处理其余的,而不再累积对象。

(在 ARC 下,向自身添加newWithNumber方法ClassB通常是一个好主意。)

于 2013-09-10T20:30:34.653 回答
1

您问题中的关键词是“OLD”。旧答案不再相关。

这就是 ARC 的用途。

您不再需要担心任何内存管理。

如果 ARC 告诉你不要这样做……不要这样做。

在这种情况下,您不需要自动释放。

于 2013-09-10T19:45:06.160 回答