4

I have a question on how to handle a CGImageRef as a synthesized property of a class. If I define an CGImageRef with

@property (nonatomic, retain) CGImageRef image;

then the compiler complains that "retain" cannot be used here. If I leave out the retain, then I assume "assign" is used instead, and I need to do the retain myself when I set the property:

self.image = CGImageRetain ( cgimage );

then I get an "Potential leak" warning when running Analyze. Can I safely ignore this warning? Or does the synthesize code do an implicit CGRetain anyways, even though no "retain" is specified in the property definition?

fishinear
  • 6,101
  • 3
  • 36
  • 84

3 Answers3

9

What you want to do is add an annotation to the property that the type really can be retained.

Change the property declaration to

@property (nonatomic, retain) CGImageRef image __attribute__((NSObject));

Note that this will only generate the getters and setters for you, the instance variable itself is not ARC controlled. Specifically, this means that you must release it in dealloc, and that you need to use proper retain and release when assigning directly to the instance variable.


A better approach may be to use a typedef:

typedef CGImageRef CGImageObject __attribute__((NSObject));
@property (nonatomic, retain) CGImageObject image;

In this case, the instance variable is controlled by ARC, and so you must not release it in dealloc, and direct assignments to the instance variable are handled by ARC as well.


For reference, see the specification, specifically section 4.1.1:

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 than assign. 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.

and section 3:

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))
fishinear
  • 6,101
  • 3
  • 36
  • 84
Joshua Weinberg
  • 28,598
  • 2
  • 97
  • 90
  • Thanks for the quick response, Joshua.That does work, but is not pretty. I assume it instructs the compiler to treat the property as an NSObject – fishinear Nov 28 '11 at 14:47
  • @fishinear: It's not pretty because a `CGImageRef` isn't an NSObject, and you're insisting on treating it as one. That's totally fine, of course, since it's toll-free bridged to the UIImage type. But the syntax of the language can't let you get away with that completely unscathed. Hence either using an explicit foundation retain, a cast, or the property decoration. – Ben Zotto Nov 28 '11 at 16:46
  • 2
    CGImage is absolutely NOT toll free bridged to UIImage. But all CFTypes can be treated as NSObjects for memory management. – Joshua Weinberg Nov 28 '11 at 16:50
  • Yeah, that's what I meant. ;) – Ben Zotto Nov 30 '11 at 08:10
  • Here is a list of all objects that have toll-free bridging (CGImageRef isn't on there): http://developer.apple.com/library/ios/#documentation/CoreFoundation/Conceptual/CFDesignConcepts/Articles/tollFreeBridgedTypes.html – Aaron Brager Feb 19 '13 at 19:45
  • This answer is actually incorrect. Putting `__attribute__((NSObject))` in a property declaration does not make the underlying instance variable ARC-managed, according to the ARC specification. – user102008 Mar 06 '18 at 02:49
  • @user102008 Yes it does. The `__attribute__((NSObject))` says that the variable must be handled as a retainable object pointer for ARC purposes. See https://clang.llvm.org/docs/AutomaticReferenceCounting.html#id9 – fishinear Mar 06 '18 at 18:27
  • @fishinear: See https://clang.llvm.org/docs/AutomaticReferenceCounting.html#property-declarations "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 than `assign`. 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." – user102008 Mar 06 '18 at 20:51
  • @fishinear: A `typedef` marked with `__attribute__((NSObject))` makes that typdef a retainable object pointer type. But there is no `typedef` here. – user102008 Mar 06 '18 at 20:53
  • @user102008 OMG, you are actually correct. I have edited the answer now to point out the limitations, and to add the typedef approach. – fishinear Mar 07 '18 at 02:31
1

I don't like to instruct the compiler when compiling. I think it's ugly. I'd override the methods myself.

@interface MyClass : NSObject {
    CGImageRef _image;
}
@property (nonatomic, assign) CGImageRef image;
@end

@implementation MyClass
- (void)setImage:(CGImageRef)i {
    if(_image != i) {
        CGImageRelease(_image);
        _image = CGImageRetain(i);
    }
}

- (CGImageRef)image {
    return _image;
}
@end
v1Axvw
  • 3,054
  • 3
  • 26
  • 40
  • 1
    You have a funny idea of ugly vs not ugly :) The property in this case is breaking its own contract that you specified. It is not an assign property at all. You're subverting the tools. – Joshua Weinberg Nov 28 '11 at 16:50
  • I agree with Joshua. Also, I like the idea of "synthesize", that I do not need to think about something as basic as this. – fishinear Nov 28 '11 at 17:07
  • @Joshua, you're probably right about the ugly thing. It's not correct to declare something as assign, and then implement it as retain. What do you think about not using properties at all, and just specifying a setter and a getter method in the interface? – v1Axvw Dec 02 '11 at 19:31
  • 1
    In this day and age I'm not sure why you'd not define the property and let the runtime do the right thing. – Joshua Weinberg Dec 02 '11 at 19:36
  • @JoshuaWeinberg That's true, but I'm still not feeling good about instructing the compiler. It's probably just me. – v1Axvw Dec 02 '11 at 19:49
  • 1
    All you do as a developer is instruct the compiler. Every line you type is instructing the compiler of something. Adding an attribute to a declaration is no different than adding extern, static, __block, volatile and so on. – Joshua Weinberg Dec 02 '11 at 19:52
-1

How about this?

@property (nonatomic, setter=setImage:) CGImageRef image;

(void)setImage:(CGImageRef)image {
    if (_image != image) {
        CGImageRelease(_image);
        _image = CGImageRetain(image);
    }
}
wcochran
  • 10,089
  • 6
  • 61
  • 69