在ARC(自动引用计数)的大多数情况下,我们根本不需要考虑Objective-C对象的内存管理。不允许再创建NSAutoreleasePools了,但是有一个新的语法:

@autoreleasepool {
    …
}

我的问题是,当我不应该手动释放/自动释放时,为什么我还需要这个?


编辑:简单总结一下我从所有的回答和评论中得到的东西:

新语法:

@autoreleasepool{…}是用于

NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
…
[pool drain];

更重要的是:

ARC uses autorelease as well as release. It needs an auto release pool in place to do so. ARC doesn't create the auto release pool for you. However: The main thread of every Cocoa app already has an autorelease pool in it. There are two occasions when you might want to make use of @autoreleasepool: When you are in a secondary thread and there is no auto release pool, you must make your own to prevent leaks, such as myRunLoop(…) { @autoreleasepool { … } return success; }. When you wish to create a more local pool, as @mattjgalloway has shown in his answer.


当前回答

关于这个主题似乎有很多困惑(至少有80人可能对此感到困惑,并认为他们需要在他们的代码中添加@autoreleasepool)。

如果一个项目(包括它的依赖项)只使用ARC,那么@autoreleasepool永远不需要使用,也不会做任何有用的事情。ARC将在正确的时间处理释放对象。例如:

@interface Testing: NSObject
+ (void) test;
@end

@implementation Testing
- (void) dealloc { NSLog(@"dealloc"); }

+ (void) test
{
    while(true) NSLog(@"p = %p", [Testing new]);
}
@end

显示:

p = 0x17696f80
dealloc
p = 0x17570a90
dealloc

一旦值超出范围,每个测试对象就会被释放,而不需要等待自动释放池被退出。(同样的事情发生在NSNumber的例子中;这只是让我们观察dealloc。)ARC不使用自动释放。

@autoreleasepool仍然被允许的原因是对于混合的ARC和非ARC项目,这些项目还没有完全过渡到ARC。

如果你调用非arc代码,它可能返回一个自动释放的对象。在这种情况下,上面的循环将会泄漏,因为当前的自动释放池将永远不会退出。这就是您希望在代码块周围放置@autoreleasepool的地方。

但是如果你已经完全完成了ARC转换,那么忘记autoreleasepool吧。

其他回答

这是因为您仍然需要向编译器提供提示,说明何时自动释放对象超出作用域是安全的。

引自https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/MemoryMgmt/Articles/mmAutoreleasePools.html:

Autorelease Pool Blocks and Threads Each thread in a Cocoa application maintains its own stack of autorelease pool blocks. If you are writing a Foundation-only program or if you detach a thread, you need to create your own autorelease pool block. If your application or thread is long-lived and potentially generates a lot of autoreleased objects, you should use autorelease pool blocks (like AppKit and UIKit do on the main thread); otherwise, autoreleased objects accumulate and your memory footprint grows. If your detached thread does not make Cocoa calls, you do not need to use an autorelease pool block. Note: If you create secondary threads using the POSIX thread APIs instead of NSThread, you cannot use Cocoa unless Cocoa is in multithreading mode. Cocoa enters multithreading mode only after detaching its first NSThread object. To use Cocoa on secondary POSIX threads, your application must first detach at least one NSThread object, which can immediately exit. You can test whether Cocoa is in multithreading mode with the NSThread class method isMultiThreaded.

...

在自动引用计数(ARC)中,系统使用相同的方法 引用计数系统作为MRR,但它插入适当的内存 管理方法在编译时为您调用。你很坚强 鼓励在新项目中使用ARC。如果你使用ARC,就有 通常不需要理解底层实现 在本文档中描述,尽管在某些情况下可能是 有帮助的。有关ARC的更多信息,请参见过渡到ARC发行说明。

ARC并没有摆脱保留、释放和自动释放,它只是为你添加了所需的。所以仍然有保留的调用,仍然有释放的调用,仍然有自动释放的调用,仍然有自动释放池。

他们对新的Clang 3.0编译器和ARC所做的另一个改变是,他们用@autoreleasepool编译器指令取代了NSAutoReleasePool。NSAutoReleasePool一直是一个特殊的"对象"他们这样做是为了使用它的语法不会与对象混淆所以它通常更简单一点。

所以基本上,你需要@autoreleasepool,因为仍然有自动释放池需要担心。您只是不需要担心添加自动释放调用。

使用自动释放池的示例:

- (void)useALoadOfNumbers {
    for (int j = 0; j < 10000; ++j) {
        @autoreleasepool {
            for (int i = 0; i < 10000; ++i) {
                NSNumber *number = [NSNumber numberWithInt:(i+j)];
                NSLog(@"number = %p", number);
            }
        }
    }
}

当然,这是一个非常做作的例子,但是如果你在外部for循环中没有@autoreleasepool,那么你将在以后释放100000000个对象,而不是每次释放10000个对象。

更新: 也可以看到这个答案- https://stackoverflow.com/a/7950636/1068248 -为什么@autoreleasepool与ARC无关。

更新: 我研究了一下这里发生的事情,并把它写在了我的博客上。如果你看一下这里,你会清楚地看到ARC在做什么,以及新样式@autoreleasepool以及它如何引入一个作用域,被编译器用来推断哪些保留、释放和自动释放是必要的。

@autoreleasepool不会自动释放任何东西。它创建了一个自动释放池,这样当到达块的末尾时,任何在块处于活动状态时被ARC自动释放的对象都将被发送释放消息。苹果的高级内存管理编程指南是这样解释的:

在自动释放池块的末尾,在块内接收到自动释放消息的对象将被发送一个释放消息——在块内每次发送一个自动释放消息的对象都会收到一个释放消息。

博士TL;

为什么ARC仍然需要@autoreleasepool ?

@autoreleasepool被Objective-C和Swift用来处理内部的autorelese

当你使用纯Swift并分配Swift对象时- ARC处理它

但如果你决定调用/使用Foundation/Legacy Objective-C代码(NSData, Data),其中使用autorelese,则在救援中使用@autoreleasepool

//Swift
let imageData = try! Data(contentsOf: url)

//Data init uses Objective-C code with [NSData dataWithContentsOfURL] which uses `autorelese`

长回答

Mrc arc gc

手动引用计数(MRC)或手动保留-释放(MRR)作为开发人员,您需要对对象上的引用进行手动计数

自动引用计数(ARC)在iOS v5.0和OS X Mountain Lion的xCode v4.2中引入

垃圾收集(GC)在Mac OS上可用,在OS X Mountain Lion中已弃用。必须转移到ARC

MRC和ARC中的引用计数

//MRC
NSLog(@"Retain Count: %d", [variable retainCount]);

//ARC
NSLog(@"Retain Count: %ld", CFGetRetainCount((__bridge CFTypeRef) variable));

堆中的每个对象都有一个整数值,该值表示在该对象上指出了多少引用。当它等于0时,对象被系统释放

分配对象 使用引用计数 释放对象。当retainCount == 0时调用deinit

MRC

A *a1 = [[A alloc] init]; //this A object retainCount = 1
    
A *a2 = a1;
[a2 retain]; //this A object retainCount = 2

// a1, a2 -> object in heap with retainCount

释放对象的正确方法:

如果只有这个-悬空指针。因为它仍然可以指向堆中的对象并且可以发送消息 = nil如果只有此-内存泄漏。不会调用Deinit

A *a = [[A alloc] init]; //++retainCount = 1
[a release]; //--retainCount = 0
a = nil; //guarantees that even somebody else has a reference to the object, and we try to send some message thought variable `a` this message will be just skipped

使用引用计数(对象所有者规则):

(0 -> 1) alloc, new, copy, mutableCopy (+1) retain You are able to own an object as many times as you need(you can call retain several times) (-1) release If you an owner you must release it. If you release more than retainCount it will be 0 (-1) autorelease Adds an object, which should be released, to autorelease pool. This pool will be processed at the end of RunLoop iteration cycle(it means when all tasks will be finished on the stack)[About] and after that release will be applied for all objects in the pool (-1) @autoreleasepool Forces process an autorelease pool at the end of block. It is used when you deal with autorelease in a loop and want to clear resources ASAP. If you don't do it your memory footprint will be constantly increasing

当你在方法调用中分配一个新对象并返回它时,Autorelease被使用

- (B *)foo {
    B *b1 = [[B alloc] init]; //retainCount = 1

    //fix - correct way - add it to fix wrong way
    //[b1 autorelease];

    //wrong way(without fix)
    return b; 
}

- (void)testFoo {
    B *b2 = [a foo];
    [b2 retain]; //retainCount = 2
    //some logic
    [b2 release]; //retainCount = 1
    
    //Memory Leak
}

@autoreleasepool例子

- (void)testFoo {
    for(i=0; i<100; i++) {
        B *b2 = [a foo];
        //process b2
    }
}

ARC

ARC最大的优点之一是它可以在编译时自动插入、保留、释放、自动释放,作为开发人员,你不需要再管它了

启用/禁用弧

//enable
-fobjc-arc
//disable
-fno-objc-arc

从优先级高到优先级低的变体

//1. local file - most priority
Build Phases -> Compile Sources -> Compiler Flags(Select files -> Enter) 

//2. global
Build Settings -> Other C Flags(OTHER_CFLAGS)

//3. global
Build Settings -> Objective-C Automatic Reference Counting(CLANG_ENABLE_OBJC_ARC)

检查ARC是否开启/关闭

使用预处理器__has_feature函数

__has_feature(objc_arc)

编译时

// error if ARC is Off. Force to enable ARC
#if  ! __has_feature(objc_arc)
    #error Please enable ARC for this file
#endif

//or

// error if ARC is On. Force to disable ARC
#if  __has_feature(objc_arc)
    #error Please disable ARC for this file
#endif

运行时

#if __has_feature(objc_arc)
    // ARC is On
    NSLog(@"ARC on");
#else
    // ARC is Off
    NSLog(@"ARC off");
#endif

逆向工程(适用于Objective-C)

//ARC is enabled
otool -I -v <binary_path> | grep "<mrc_message>"
//e.g.
otool -I -v "/Users/alex/ARC_experiments.app/ARC_experiments"  | grep "_objc_release"

//result
0x00000001000080e0   748 _objc_release

//<mrc_message>
_objc_retain
_objc_release
_objc_autoreleaseReturnValue
_objc_retainAutoreleaseReturnValue
_objc_retainAutoreleasedReturnValue
_objc_storeStrong

工具迁移Objective-C MRC到ARC

ARC生成错误,您应该手动删除保留,释放,自动释放和其他问题

Edit -> Convert -> To Objective-C ARC...

新Xcode与MRC

如果你启用了MRC,你会得到下一个错误(警告)(但是构建会成功)

//release/retain/autorelease/retainCount
'release' is unavailable: not available in automatic reference counting mode
ARC forbids explicit message send of 'release'