Ios 在方法中创建的圆弧和释放对象

Ios 在方法中创建的圆弧和释放对象,ios,objective-c,memory-management,automatic-ref-counting,Ios,Objective C,Memory Management,Automatic Ref Counting,我偶然发现了一个在别处找不到答案的问题。当我调用一个方法,该方法返回一个指向后来使用的对象的指针,并最终设置为nil时,它仍然被分配到内存中(根据工具)。我使用的是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

我偶然发现了一个在别处找不到答案的问题。当我调用一个方法,该方法返回一个指向后来使用的对象的指针,并最终设置为nil时,它仍然被分配到内存中(根据工具)。我使用的是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*实例=[[ClassB alloc]initWithNumber:counter]


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

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

这就是ARC的用途

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

如果ARC告诉你不要这么做。。。不要


在这种情况下,您不需要自动删除。

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

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

从文件中:

3.2.3未恢复的返回值

一种方法或函数,它返回一个可保留的对象类型,但不返回 不返回保留值必须确保对象仍然有效 越过返回边界

从此类函数或方法返回时,ARC保留该值 在对return语句求值时,将所有 然后平衡保留,同时确保 价值跨越调用边界存在。 在最坏的情况下,这可能涉及自动释放,但调用方不得假定该值为 实际上在自动释放池中


makeClassBWithNumber
不是alloc、copy、init、mutableCopy或new
方法,因此返回一个未恢复的返回值。

不同之处在于
+alloc
返回一个带有+1 retain的对象,该对象将在其作用域结束时与释放平衡,因此立即取消分配
+make…
返回具有+1保留和匹配自动释放的对象。自动释放池在耗尽时将发送一条
release
消息。由于您在“为真”时仍处于循环中,因此自动释放池永远不会耗尽,并且您会积累内存

解决方案是为循环提供一个自动释放池:

while (true) {
    @autoreleasepool { // <== Add an autorelease block here.
      ClassB *instance = [self makeClassBWithNumber:counter];
      //NSLog(@"%d", [instance getNumber]);
      NSLog(@"%d", [instance number]); // Fix naming; do not prefix accessors with `get`
      [NSThread sleepForTimeInterval:0.01];
      // instance = nil; // Does nothing in this loop.
      counter++;
    }
}
while(true){

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

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

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

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

在ARC下,更好的解决方案可能是遵循命名约定,在这种情况下,您希望使用
new
模式-
new
alloc
+
init
的标准模式,使用一种方法。因此,将
makeClassBWithNumber
重命名为
newClassBWithNumber

// *create* a new ClassB object
- (ClassB *) newClassBWithNumber:(int)number
{
   return [[ClassB alloc] initWithNumber:number];
}
这表示该方法返回调用方拥有的对象,它是一个“创建”方法,ARC将处理其余的对象,而不再累积对象


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

因此对象不会被释放,因为我们在无限循环中创建它们?如果循环受到限制,那么当函数退出时,所有创建的对象都会被释放?@Robert:自动释放池时,自动释放的对象会被释放(它们是在其中创建的)结束。-有一个自动释放池与主事件循环相关联,因此如果您有一个有限的循环,并且程序控制返回到主事件循环,则对象被释放。这是比使用自动释放池更好的解决方案。我不知道这样的命名约定对整个ARC有这么大的影响。@Robert-命名约定在Obj-C中一直非常重要,在MRC下,他们提供了重要的信息,以便程序员知道何时保留和释放。ARC本身依靠属性来传递所需的信息,命名约定规定了这些属性的默认值。自动释放帮助人类处理MRC,尽管它仍然有它的用途在ARC下,你应该少去想它。