Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

strong @property with __attribute__((NSObject)) for a CF type doesn't retain

Tags:

UPDATE: This issue has been fixed as of Xcode 4.6!

This technique now works as intended again. However, make sure to read the notes at the top of Rob Napier's excellent answer before you use it in your code.

ORIGINAL POST

(ARC, Xcode 4.3.1, iOS 5.1)

I have a strong property of a CF type (CGImage) that I want to be automatically managed by ARC using __attribute__((NSObject)) (as in having retain & release in the synthesized setter, and it being nil'ed in dealloc), but it doesn't work: the object is not retained when I assign the property.

A minimal example to reproduce:

@interface TestClass : NSObject @property (nonatomic, strong) __attribute__((NSObject)) CFStringRef str; @end  // ...In some function CFStringRef str = (__bridge CFStringRef)[NSString stringWithFormat:@"%g", 2.5]; NSLog(@"%ld", CFGetRetainCount(str)); TestClass *obj = [[TestClass alloc] init]; obj.str = str; NSLog(@"%ld", CFGetRetainCount(str)); 

Which prints '1' twice.

Now the strange thing is that (although I'm not sure of this) I think it worked properly before I updated to iOS 5.1 & Xcode 4.3.1 (from iOS 5 & Xcode 4.2), and with it switched from gdb to lldb. Can someone who hasn't upgraded (or knows how to change back the compiler) perhaps confirm?

like image 419
Patrick Pijnappel Avatar asked Mar 13 '12 13:03

Patrick Pijnappel


1 Answers

EDIT2 (Mar 2013) FYI for those interested in this technique, ARC documentation for clang includes the following note:

The use of __attribute__((NSObject)) typedefs is not recommended. If it’s absolutely necessary to use this attribute, be very explicit about using the typedef, and do not assume that it will be preserved by language features like __typeof and C++ template argument substitution.

Rationale

Any compiler operation which incidentally strips type “sugar” from a type will yield a type without the attribute, which may result in unexpected behavior.


EDIT The below is interesting, but probably irrelevant. This is a bug and you should open a radar. As noted by @lnafziger, this is legal and is supposed to be honored. The bug is that it is not honored when you include nonatomic. If you remove nonatomic, then it works. Nothing in the nonatomic definition suggests that this is by design.


This is kind of clever, but I think I see why it isn't working. You can confirm, btw, that it isn't working by generating the assembler and noting that setStr: does not call objc_storeStrong(). It does a simple assign.

The problem is that your property definition does not conform to the definition of a retainable object pointer (emphasis added):

A retainable object pointer (or retainable pointer) is a value of a retainable object pointer type (retainable type). There are three kinds of retainable object pointer types:

  • block pointers (formed by applying the caret (^) declarator sigil to a function type)
  • Objective-C object pointers (id, Class, NSFoo*, etc.)
  • typedefs marked with __attribute__((NSObject))

Did you create a typedef as specified? No you did not. OK, so how would we do this?

typedef __attribute__((NSObject)) CFStringRef MYStringRef;  @interface TestClass : NSObject @property (nonatomic, strong) MYStringRef str; @end  ... the rest of your code ... 

This will print "1" and then "2" as I assume you expect. Scares me for unspecified reasons, but looking at the assembler output, everything seems fine and I can't think of any specific problem or violation here.

You could be justified in opening a radar for this. The fact that a typedef is not treated the same as a specified type is surprising at least, even if documented.

EDIT: Noting @lnafziger's comment from the ObjC Programming Language, there is definitely either an error in the ARC spec/implementation or an error in the linked document, so one of them should be fixed.

like image 77
Rob Napier Avatar answered Sep 28 '22 09:09

Rob Napier