4

我正在尝试将手动保留发布代码转换为 ARC。

当我有一个 Objective-C 便利构造函数,其返回指针值存储在 CFTypeRef 中时,我正在努力找出免费桥接的正确方法。

现有代码,使用 MRR:

@interface SourceItemCell UITableViewCell
{
  CATextLayer *mSourceText;
}

@implementation SourceItemCell
- (id)init
{
  self = [super init];
  mSourceText = [CATextLayer layer];

  // key line I'm wondering about:
  mSourceText.font = [UIFont fontWithName:@"HelveticaNeue" size:12.0];

  [[self contentView].layer addSublayer:mSourceText];
  return self;
}

为了让您不必查找文档,CATextLayer 的字体属性是 CFTypeRef 类型。

看来我的选择是:

mSourceText.font = (__bridge CFTypeRef)[UIFont fontWithName:@"HelveticaNeue" size:12.0];

或者:

mSourceText.font = (__bridge_transfer CFTypeRef)[UIFont fontWithName:@"HelveticaNeue" size:12.0];

或者:

mSourceText.font = (__bridge_retained CFTypeRef)[UIFont fontWithName:@"HelveticaNeue" size:12.0];

这是我的想法。我发现的最清晰的免费桥接指南是http://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html。有一个从 Objective-C 类型转换为 C 类型的类似示例,他写道:

通过使用 __bridge_retained,我们可以告诉 ARC 将所有权从系统转移到我们手中。由于所有权已转移,我们现在负责在完成对象后释放它,就像任何其他 CF 代码一样

...否则,如果我们只使用 __bridge,ARC 将不会做任何努力来保留我们 CFTypeRef 帐户上的内存。

所以这是我认为最明智的方法:

mSourceText.font = (__bridge_retained CFTypeRef)[UIFont fontWithName:@"HelveticaNeue" size:12.0];
... // At some later point
CFRelease(mSourceText.font);

现在,如果那是正确的,我仍然不清楚何时可以确定释放它是安全的,但如果我从未释放,它至少只是一个小的内存泄漏,对吧?

总之,我的实际问题是:

  1. 我建议的代码正确吗?
  2. 我应该在哪里 CFRelease 这个对象?在 SourceItemCell 的 dealloc 函数中?

这就是为什么我认为相关问题没有回答我的问题的原因:

  1. 如果我将自动释放的对象桥接到 Core Foundation,我必须使用 __bridge 或 __bridge_retained 吗?我不确定将 Objective-C 便利构造函数显式保存到上一行的 Objective-C 变量是否重要。此外,批准的答案说只使用 __bridge_retained “如果你想管理 C 对象的生命周期”,我认为这是错误的......我感觉很多人都在使用 __bridge_retained 因为他们必须自己管理生命周期.
  2. Where and how to __bridge Approved answer有有用的总结,但侧重于非保留示例。

PS。请不要因为我使用 Helvetica 就评判我... :)

编辑:

当我使用 __bridge_retained 并进行静态分析器时,我收到以下投诉:

静态分析仪投诉

“属性返回一个具有 +0 保留计数的核心基础对象。调用者此时不拥有的对象的引用计数不正确递减。”

(我相信 mDelegate 和 IS_ARC 行与这个问题无关。)

所以有些东西我基本上没有正确理解......

4

2 回答 2

4

首先,我想知道原始MRR代码是否正确。根据文档,您不能将UIFont对象分配给 a 的font属性CATextLayer,而是 aCTFontRef或 a CGFontRef。这样的事情应该可以正常工作:

 CGFontRef font = CGFontCreateWithFontName(CFSTR("HelveticaNeue"));
 mSourceText.font = font;
 CGRelease(font);
 mSourceText.fontSize = 12.0;

为了回答您关于桥接的问题,我们假设UIFont *a转换为正确的CGFontRef(我很确定不是!)。然后你会使用__bridge

 mSourceText.font = (__bridge CGFontRef)[UIFont fontWithName:@"HelveticaNeue" size:12.0];

原因是CATextLayerin 中的对象mSourceText会保留字体本身,所以我们不需要这样做。

如果我们要CGFontRef在两者之间存储,我们必须小心。像这样的代码很危险:

CGFontRef fontRef = (__bridge CGFontRef)[UIFont fontWithName:@"HelveticaNeue" size:12.0];
mSourceText.font = fontRef;

ARC 可能会UIFont在第一条和第二条语句之间释放对象,以便fontRef指针指向一个已释放的对象。如果我们想在两个语句中编写代码,我们必须保留该对象,以便它能够被分配足够长的时间,然后再释放它:

CGFontRef fontRef = (__bridge_retained CGFontRef)[UIFont fontWithName:@"HelveticaNeue" size:12.0];
mSourceText.font = fontRef;
CFRelease(fontRef);
于 2013-05-30T14:58:43.897 回答
2

我同意塔莫的回答。

我建议您始终分别使用__bridge_transfer__bridge_retained:CFBridgingRelease()和的覆盖函数CFBridgingRetain()。它们有助于澄清你的想法。

您只能CFBridgingRelease()在您有权使用的情况下使用CFRelease(). 你应该只CFBridgingRetain()在有意义的地方使用CFRetain().

该方法+[UIFont fontWithName:size:]不给你返回对象的所有权,所以你无权释放它,所以你肯定不CFRelease()会这样做,同样你也不应该这样CFBridgingRelease()做。

现在考虑另一种情况:在将它传递给属性设置器之前,您CFRetain()还是一个对象?-retain让我们考虑一个稍微不同的情况。想象一个具有fonttype 属性的类UIFont*,而不是任何 Core Foundation 类型。你会这样做吗(在 MRR 下):

someObject.font = [[UIFont fontWithName:@"HelveticaNeue" size:12.0] retain];

?

希望你知道你不能。您没有适当的方法来释放该对象以平衡该保留。(而且,不,稍后再做[someObject.font release]是不正确的。属性可能不会从传递给 setter 的 getter 返回相同的对象。setter 可能会根据传入的值进行复制或计算新值。或者做任何它喜欢的东西。)

无论如何,一旦你意识到你不会-retain在那种情况下,你就会意识到你不会CFRetain()而且你不应该CFBridgingRetain()

最后,另一种思考方式是内存管理的局部性。setter 的调用者不负责使传入的对象保持不变。setter 的实现对此负责,如果它确实最终保持对传入对象的引用(它可能不会)。在编写方法或类时,必须为该方法或类而不是任何其他方法或类处理内存管理。您应该期望其他方法或类负责自己的职责。(该原则也是允许混合 ARC 编译代码和 MRR 代码的原因。)

于 2013-06-01T14:17:05.857 回答