I want a property backed by a strong reference but which has a Core Foundation object type (not Objective-C object type) in ARC. I believe the proper way to do this was to mark it with __attribute__((NSObject))
and ARC will manage it just like an Objective-C object type property (i.e. release and retain on assign, and release when the containing object is dealloc
ed):
@interface Foo : NSObject
@property (nonatomic, strong) __attribute__((NSObject)) CFArrayRef bar;
@end
@implementation Foo
// property bar is auto-synthesized
@end
(I know CFArray
is toll-free bridged to NSArray
. This is just for the sake of an example. The real use case is for a Core Foundation type that does not have toll-free bridging.)
But when I put an object into the bar
property of a Foo
object and let the Foo
object go out of scope and get dealloc
ed, the object in bar
is leaked because it is not released when the Foo
object is deallocated, as it would with a regular Objective-C object type property:
void someFunc(void) {
Foo *foo = [[Foo alloc] init];
CFArrayRef arr = CFArrayCreateMutable(NULL, 42, &kCFTypeArrayCallBacks);
foo.bar = arr;
CFRelease(arr);
}
The code compiles without any warnings or errors, and passes Analyze with no issues. The Leaks instrument detects that the mutable array was leaked.
Is this a compiler bug? I am using the latest Xcode (8.3.3).
There is curious wording in the ARC specification, when defining retainable object pointer the last kind is defined as:
typedefs marked with
__attribute__((NSObject))
Note that this uses typdefs and not declarations. The phrasing is used again a little further in the spec. This almost seems to suggest you need to use a typedef
for the attribute to be recognised, which would be a curious design.
Unfortunately when it comes to property declarations the spec says (emphasis added):
Applying
__attribute__((NSObject))
to a property not of retainable object pointer type has the same behavior it does outside of ARC: it requires the property type to be some sort of pointer and permits the use of modifiers other thanassign
. These modifiers only affect the synthesized getter and setter; direct accesses to the ivar (even if synthesized) still have primitive semantics, and the value in the ivar will not be automatically released during deallocation.
This does appear to explain the behaviour you are seeing - the design is half-baked, the getter/setter adopt the semantics but deallocation doesn't.
Given the spec wording your one hope for automatic support seems to be to first create an attributed typedef for CF type and then use that in the property declaration... Weird if true, but it fits the curious wording... Sorry can't check this at the moment, but I'm guessing disappointment will be the outcome. Try assigning nil
to the property in the dealloc
as that uses the setter which (according to the spec) has the correct semantics.
HTH