0

根据Bavarious 对这个 SO question 的回答,如果您使用 LLVM/clang 构建,我相信这@autoreleasepool现在是一个 Objective-C 语言功能。

在这种情况下,如何重写以下代码以在非 ARC 环境中使用 an@autoreleasepool而不是 an ?NSAutoreleasePool

[NSAutoreleasePool addObject:anObject]; // (*)

背景:我本质上想编写一个-autorelease以任何方式NSAutoreleasePool类交互的自定义​​实现:

@autoreleasepool {
    SomeCls *obj = [[SomeCls alloc] init];
    [obj autorelease]; // Does not go through an NSAutoreleasePool object
    // ...
}
4

5 回答 5

3

@autoreleasepool { }是一种新的语言功能,旨在避免在函数的每个退出点耗尽当前池的需要。现在,不必写:

void f(void) {
    //Make a new pool
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc]init];
    //Every inner scope that exits prematurely requires a drain
    if (life_is_good) {
        for (int i = 0; i < 1000; i++) {
            NSObject *obj = [[[NSObject alloc]init]autorelease];
            //breaks out of the loop, and the function, so drain the pool
            if (life_is_bad) {
                [pool drain];
                return;
            }
        }
        //Life gets bad below here, so return
        [pool drain];
        return;
    }
    //end of function requires drain.
    [pool drain];
}

您可以将整个函数包装在@autoreleasepool { }指令中,编译器将在函数返回的地方插入适当的排水管:

void f(void) {
    //Make a new pool
    @autoreleasepool {
        if (life_is_good) {
            for (int i = 0; i < 1000; i++) {
                NSObject *obj = [[[NSObject alloc]init]autorelease];
                //breaks out of the loop, and the function, so drain the pool
                if (life_is_bad) {
                    //[pool drain]; auto-drain
                    return;
                }
            }
            //[pool drain]; auto-drain
            return;
        }
        //[pool drain]; auto-drain
    }
}

我不知道您为什么要放弃自动池机制,因为它为您处理大量工作(页面管理、每个线程池管理、及时有效地释放对象),但它不应该让一个原始的 NSAutoreleasePool 启动并运行起来很麻烦。任何自动释放池都需要一个页面管理器(malloc()realloc())、指针存储(一个简单的数组)和某种方法来检测当前使用它的范围是否已经退出(-drain)。一个简单的池可能只是一个线程安全的单例,但要注意使用该实现一次尝试插入和删除多少对象。如果您尝试为太多对象分配太多热页,则可能会出现段错误。

于 2013-07-07T13:54:39.350 回答
1

虽然我已经将答案标记为已接受,并在当时提供了赏金,但我已经找到了我正在寻找的真正答案,并意识到我的问题并不是很清楚——主要是因为我不知道该问什么。

如何与@autoreleasepool 交互:

当编译器遇到一个@autoreleasepool { }块时,它会自动插入两个函数调用。在块的开头,它插入 C 函数调用:

void *_objc_autoreleasePoolPush(void);

@autoreleasepool块结束时(包括在其中遇到returnor时 - 但不是根据LLVM 文档break抛出异常时)编译器插入 C 函数调用:

_objc_autoreleasePoolPop(void *ctxt); // ctxt is Apple's label for the param

我相信该void *ctxt参数是当前@autoreleasepool开始位置的一个指标(实际上只在 Apple 的实现中有用 - 请参见此处)。无论如何,我发现它在自定义实现中很容易被忽略。

选择-autorelease器:

本质上,方法是这样的:

  • _objc_autoreleasePoolPush(). 对我来说,使用 Stack 数据结构似乎最容易,但 YMMV。
  • 确保此对象在选择器的范围内-autorelease,或者您有一种安全的方式来访问它。
  • 将对象添加到内部的自动释放池对象(以任何方式)-autorelease
  • _objc_autoreleasePoolPop(void *)中,将-release消息发送到自动释放池对象中的每个项目,直到_objc_autoreleasePoolPush()到达设置的标记(或者您到达池的底部)。然后进行任何其他所需的清理。

关于线程的最后说明:

Objective-C@autoreleasepools应该是线程本地的。也就是说,每个线程都有自己的(如果需要的话)。因此,_objc_autoreleasePoolPush()必须有某种方法来确定当前线程是否已经有一个活动的自动释放池,如果没有则创建该对象。

类似地,这就是为什么在创建线程时,线程必须先打开一个@autoreleasepool { }块,然后再做任何其他事情(在 Objective-C 中),它必须做的最后一件事是关闭该@autoreleasepool块(隐式通过breakreturn或显式 fall-通过)。

最后的三个观察:

  1. 如果您希望实现自己的自动释放池对象,您将需要某种形式的线程本地存储来存储它。
  2. 关于线程的最后一点是为什么每个 Objective-C 程序都必须@autoreleasepool { }main().
  3. 这就是为什么 Apple 要求您使用NSThreadGCD而不是 DIY 线程 - 他们会为您处理所有这些!
于 2014-01-09T02:12:00.843 回答
1

根据 Bavarious 对这个 SO question 的回答,如果您使用 LLVM/clang 构建,我相信 @autoreleasepool 现在是一个 Objective-C 语言功能。

正确的。

在这种情况下,如何重写以下代码以在非 ARC 环境中使用 @autoreleasepool 而不是 NSAutoreleasePool?

[NSAutoreleasePool addObject:anObject]; // (*)

Doc: 通常你不会直接调用这个方法——而是将 autorelease 发送给对象。

@autoreleasepool {
 id anObject = ...;
 [anObject autorelease];
}

背景:我基本上想编写一个自定义实现 -autorelease 不以任何方式与 NSAutoreleasePool 类交互:

你甚至不能NSAutoreleasePool再继承(以一种有意义的方式)。除非您从不将对象传递给其他可以自由自动释放的 API,否则您的对象最终会出现在当前的自动释放池中。今天的这种机制甚至不是基于对象的。

如果这是不可能的,我愿意接受不涉及 NSAutoreleasePool 的解决方法建议。

不确定您要解决什么问题,但您可以使用以下方法延长生命周期:

NSMutableArray * fauxAutoreleasePool = NSMutableArray.new;
id anObject = ...;
[fauxAutoreleasePool addObject:anObject];
...
[fauxAutoreleasePool removeAllObjects]; // << explicitly drain, which also happens when the array is destroyed

当然,您可以编写自己的实现。为什么要编写自己的代码仍然是个谜,对于生产代码来说不是一个好主意(@autoreleasepool效果很好)。

于 2013-07-08T10:16:51.493 回答
0

背景:我基本上想编写一个自定义实现 -autorelease 不以任何方式与 NSAutoreleasePool 类交互:

是什么让您认为与-autorelease互动NSAutoreleasePool

只要您在 iOS 5+ 或 Mac OS X 10.7+ 上运行,两者都@autoreleasepool-[NSObject autorelease]使用运行时自动释放池管理功能,并且完全没有任何关系NSAutoreleasePool(并且NSAutoreleasePool在这些操作系统版本上只是运行时的包装器)功能)。

于 2013-07-07T18:22:07.993 回答
0

At the end of the @autoreleasepool block, the autoreleased objects are sent a release message.

If you want to reimplement the autorelease method, you may not want to directly send release messages to your NSObjectsubclass. If so, reimplementing release with some special behavior could do your business.

于 2013-07-06T10:27:25.243 回答