4

我有以下形式的ARC代码:

NSMutableData* someData = [NSMutableData dataWithLength:123]; ...

CTRunGetGlyphs(run, CGRangeMake(0, 0), someData.mutableBytes); ...

const CGGlyph *glyphs = [someData mutableBytes]; ...

...后面是从 中读取内存glyphs但不执行任何操作的代码,someData不再引用。请注意,CGGlyph 不是对象类型,而是无符号整数。

我是否必须担心someData在我完成之前可能会释放内存glyphs(实际上只是指向内部someData)?

所有这些代码都在同一个范围内(即单个选择器),glyphs并且someData都同时超出范围。

PS 在这个问题的早期草稿中,我提到了“垃圾收集”,它并不真正适用于我的项目。这就是为什么下面的一些答案将其与 ARC 下发生的事情同等对待。

4

4 回答 4

4

无论您使用 GC 还是其他人推荐的 ARC,您都可能会遇到麻烦。您正在处理的是一个内部指针,一般来说,它在 GC 或 ARC 中不被视为拥有引用NSData- 除非实现具有特殊情况。如果没有该拥有引用,GC 或 ARC 可能会删除该对象。您面临的问题是内部指针所特有的。

当你描述你的情况时,最安全的做法是抓住真正的参考。您可以通过将NSData引用分配给实例变量或static(如果您愿意,可以使用本地方法)变量nil来完成此操作,然后在完成内部指针后分配给该变量。在并发的情况下static要小心!

在实践中,您的代码可能会在 GC 和 ARC 中工作,可能更可能在 ARC 中工作,但可以想象,任何一个都可能会咬你,尤其是当编译器发生变化时。对于一个变量声明和一个额外分配的成本,您可以避免这个问题,便宜的保险。

[将此讨论视为 ARC 下生命周期短的示例。]

于 2012-09-14T20:27:27.747 回答
3

在实际的、真正的垃圾收集下,该代码可能是一个问题。一旦不再有对它们的任何引用,对象就可能被释放,如果您不再使用它,编译器可能会随时丢弃该引用。出于优化目的,范围只是对这类事物设置上限的一种方式,而不是一种绝对规定它的方式。

您可以使用NSAllocateCollectable将生命周期计算附加到 C 原始指针,尽管它很混乱且略微令人费解。

垃圾收集从未在 iOS 中实现,现在在 Mac 上已弃用(如本常见问题解答的最底部所引用),在这两种情况下都支持自动引用计数 (ARC)。ARC 添加retains 并且releases它可以看到它们是隐式需要的。遗憾的是,它可以执行一些以前不可能的巧妙技巧,例如从自动释放池中检索对象(如果它们已被用作返回结果)。所以这与垃圾收集方法具有相同的净效果——对象可以在对它的最终引用消失后的任何时候被释放。

一种解决方法是创建一个类,如:

@interface PFDoNothing

+ (void)doNothingWith:(id)object;

@end

实施它什么都不做。使用完内部存储器后,将自动释放的对象发布到它。Objective-C 的动态分派意味着编译器优化调用是不安全的——它无法知道您(或 KVO 机制或任何其他参与者)没有在运行时执行诸如方法调配之类的事情。

编辑:NSData作为一个特例,因为它提供对对象持有的内存的直接 C 级访问,至少不难找到关于 GC 情况的明确讨论。请参阅Cocoabuilder上的这个线程以获得一个非常好的线程,尽管适用与上述相同的警告,即不推荐使用垃圾收集并且自动引用计数的行为不同。

于 2012-09-14T19:06:36.313 回答
2

以下是不一定反映 Objective-C GC 支持的通用答案。然而,各种 GC 实现,包括引用计数,可以从可达性的角度来考虑,撇开怪癖不谈。


在 GC 语言中,只要对象是可达的,它就保证存在;这些强可达图的“根”可能因语言和执行环境而异。“强”的确切含义也有所不同,但通常意味着边缘是强引用。(在手动引用计数场景中,每条边都可以被认为是来自给定“所有者”的无与伦比的“保留”。)

CLR/.NET 上的 C# 就是这样一种实现,其中变量可以保留在范围内,但不能充当可达性图的“根”。查看Systems.Timer.Timer课程并寻找GC.KeepAlive

如果在长时间运行的方法中声明了计时器,请使用 KeepAlive 来防止在方法结束之前 [在计时器对象上] 发生垃圾收集。

于 2012-09-14T19:03:55.633 回答
2

截至 2012 年夏天,返回非对象类型内部指针的 Apple 对象正在发生变化。在 Mountain Lion 的发行说明中,Apple 表示:

NS_RETURNS_INNER_POINTER

返回指针(Objective C 对象类型除外)的方法已使用 clang 编译器属性 objc_returns_inner_pointer (使用 clang 编译时)进行修饰,以防止编译器积极释放那些似乎不再被引用的消息的接收者表达式,而返回的指针可能仍在使用中。

检查 NSData.h 头文件表明这也适用于 iOS 6 以后的版本。

另请注意,这NS_RETURNS_INNER_POINTER__attribute__((objc_returns_inner_pointer))clang规范中定义的,这使得

对象的生命周期将至少延长到以下最早的时间:在调用函数中最后一次使用返回的指针或从它派生的任何指针;或者自动释放池恢复到以前的状态。

注意事项:如果您使用的是比 Mountain Lion 或 iOS 6 更早的版本,则__attribute__((objc_precise_lifetime))在声明本地 NSData 或 NSMutableData 对象时,您仍然需要使用此处讨论的任何方法(例如, )。

此外,即使使用最新的编译器和 Apple 库,如果您将旧的或第三方库与不修饰其内部指针返回方法的对象一起使用,__attribute__((objc_returns_inner_pointer))您将需要使用__attribute__((objc_precise_lifetime))或使用以下对象之一来修饰此类对象的局部变量声明答案中讨论的其他方法。

于 2012-09-21T03:44:34.920 回答