4

I'm trying to convert manual retain release code to ARC.

I'm struggling to figure out the correct way to toll free bridge when I have an Objective-C convenience constructor whose return pointer value is being stored in a CFTypeRef.

Existing code, using 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;
}

To keep you from having to look up the documentation, the font property of CATextLayer is of type CFTypeRef.

It seems like my options are:

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

or:

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

or:

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

Here's my thinking. The clearest guide to toll free bridging I've found is http://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html . There's a similar example of a cast from an Objective-C type to a C type, about which he writes:

By using __bridge_retained, we can tell ARC to transfer ownership out of the system and into our hands. Since ownership is transferred, we're now responsible for releasing the object when done with it, just like with any other CF code

...otherwise, if we were only to use __bridge, ARC would not make any effort to hold onto the memory on our CFTypeRef's account.

So here is what I think is the most sensible way to go:

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

Now, if that's correct, I'm still pretty unclear on when I could be sure it's safe to release, but if I never released, it would at least be just a small memory leak, right?

In conclusion, my actual questions are:

  1. Is my proposed code correct?
  2. Where should I CFRelease this object? In the dealloc function for SourceItemCell?

Here's why I didn't think related questions answered my question:

  1. Must I use __bridge or __bridge_retained if I'm bridging an autoreleased object to Core Foundation? I'm not sure if it matters that the Objective-C convenience constructor is explicitly saved to an Objective-C variable on the previous line. Also, the approved answer says to only use __bridge_retained "if you want to manage the lifecycle" of the C object, which I think is wrong... I get the sense lots of people are using __bridge_retained because they have to manage the lifecycle themselves.
  2. Where and how to __bridge Approved answer has helpful summary, but focuses on non-retained example.

PS. Please don't judge me because I'm using Helvetica... :)

Edit:

When I use __bridge_retained, and do static analyzer, I get this complaint:

Static analyzer complaint

"Property returns a Core Foundation object with a +0 retain count. Incorrect decrement of the reference count of an object that is not owned at this point by the caller."

(The mDelegate and IS_ARC lines are, I believe, irrelevant to this issue.)

So there's something I'm fundamentally not understanding correctly...

Community
  • 1
  • 1
Ben Wheeler
  • 6,788
  • 2
  • 45
  • 55

2 Answers2

4

First, I wonder whether the original MRR code is correct. According to the documentation, you cannot assign a UIFont object to the font property of a CATextLayer, but either a CTFontRef, or a CGFontRef. Something like this should work fine:

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

To answer your question on bridging, let's assume it would be correct to cast a UIFont * to CGFontRef (which I am pretty sure is not!). Then you would use __bridge:

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

The reason is that the CATextLayer object in mSourceText will hold onto the font itself, so we don't need to do that.

We have to be careful though if we would store the CGFontRef in between. Code like this is dangerous:

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

ARC may release the UIFont object between the first and second statement, so that the fontRef pointer would point to a deallocated object. If we want to write the code in two statements, we have to retain the object so that it lives long enough to be assigned, and then release it afterwards:

CGFontRef fontRef = (__bridge_retained CGFontRef)[UIFont fontWithName:@"HelveticaNeue" size:12.0];
mSourceText.font = fontRef;
CFRelease(fontRef);
Tammo Freese
  • 10,514
  • 2
  • 35
  • 44
  • Good info. On an minor and unrelated point: if you need a literal `CFStringRef`, it's easier to use `CFSTR("HelveticaNeue")` than creating an `NSString` and `__bridge` casting it. – Rob Napier May 30 '13 at 16:31
  • Thanks for both the practical answer and the hypothetical answer :) – Ben Wheeler May 31 '13 at 17:35
  • the thing that makes me confused about your __bridge suggestion is what http://www.mikeash.com/pyblog/friday-qa-2011-09-30-automatic-reference-counting.html says about a similar case: "Adding a __bridge to the cast makes it compile, but the resulting code is dangerous... Since ARC doesn't manage the lifetime of value, it will release ownership of the object immediately, before it gets passed to UseCFStringValue, potentially causing a crash or other misbehavior." – Ben Wheeler May 31 '13 at 17:36
  • The case that Mike shows is in two statements. Then ARC may release the object between those statements. I'll edit my answer to explain the difference. – Tammo Freese Jun 01 '13 at 13:01
2

I agree with Tammo's answer.

I recommend that you always use the cover functions for __bridge_transfer and __bridge_retained: CFBridgingRelease() and CFBridgingRetain(), respectively. They help clarify your thinking.

You can only use CFBridgingRelease() in cases where you're entitled to CFRelease(). And you should only use CFBridgingRetain() where it would make sense to CFRetain().

The method +[UIFont fontWithName:size:] does not give you ownership of the returned object, so you're not entitled to release it, so surely you wouldn't CFRelease() it and similarly you shouldn't CFBridgingRelease() it.

Now consider the other case: would you CFRetain() or -retain an object before passing it to a property setter? Let's consider a slightly different case. Imagine a class with a font property of type UIFont*, not any Core Foundation type. Would you do this (under MRR):

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

?

Hopefully, you know that you must not. You have no proper way to release that object to balance that retain. (And, no, doing a later [someObject.font release] is not correct. A property may not return the same object from its getter that was passed to the setter. The setter may make a copy or compute a new value based on the value passed in. Or do anything else it likes.)

Anyway, once you realize that you wouldn't -retain in that situation you realize that you wouldn't CFRetain() and you shouldn't CFBridgingRetain().

Finally, another way to think about things is locality of memory management. The caller of a setter is not responsible for making the passed-in object stick around. The implementation of the setter is responsible for that, if indeed it ends up keeping a reference to the passed-in object (which it may not). When you are writing a method or class, you must handle memory management for that method or class, not for any other method or class. You should expect that the other method or class takes care of its own responsibilities. (That principle is also what allows the mixing of ARC-compiled and MRR code.)

Ken Thomases
  • 88,520
  • 7
  • 116
  • 154
  • Thanks, this is still giving me lots to think about! "A property may not return the same object from its getter that was passed to the setter" -- do you have any citation for that? – Ben Wheeler Feb 25 '14 at 00:35
  • There's no specific citation for that. It's just that there's no requirement imposed on the behavior of setters. For one, consider a setter that copies the passed-in object (as when there's a declared property with the `copy` attribute, but also with ad hoc properties which have copy semantics). Or consider an object that is an interface to a database. The setter may store a value in the database and the getter may query the value from the database. The passed-in object may not be kept at all. – Ken Thomases Feb 25 '14 at 04:24