strong #property with __attribute__((NSObject)) for a CF type doesn't retain - objective-c

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?

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.

Related

Objective-C init quirks

In a class interface I define some ivar
#property (strong,nonatomic) id < Protocol > initEst; // Initial estimate
This compiles without a problem but when I run the program it crashes with EXC_BAD_ACCESS and [Object .cxx_destruct] indicated by the debugger as the reason.
What is going on?
It's all about the rules of ARC automatic memory management. An initializer has special rules for how it treats the returned value: it retains and returns. See https://clang.llvm.org/docs/AutomaticReferenceCounting.html#semantics-of-init.
Objective-C in general, and ARC in particular, have some pretty strict rules about what names of methods mean. initXXX means "this is an initializer". If this isn't an initializer, don't use the init prefix.
You could turn off ARC entirely and manage memory yourself, but it's easier just to obey the conventions, and it fits better in case of interaction with other languages (such as Swift).
I have tested this some more and there seems to be three conditions for this particular quirk to show up.
In my particular case the ivar's Protocol was also the same as that of the containing class. This seems to be an additional requirement for this problem to surface (refering here to my earlier answer that did not mention this).
So to elaborate on my earlier answer. If
initXXX is an ivar
of id type
that implements a Protocol that is the same as the containing class
then the Objective-C + ARC compiler will happily compile the code but be unable to execute it.
Here is a sample of the code I used to test
#interface Dog : NSObject < Animal >
#property (nonatomic,strong) id < Animal > initState;
#end
Something like this will cause problems simply because the name starts with init. Change the name and all problems disappear.
For reference, the runtime error this generates is
Dog object overreleased while already deallocating
This snippet is pretty abstract but this may bite you in places where you need to specify some initial condition and where it is natural to name some ivar initXxx but beware, if you use Objective-C you do not have that luxury nor will the compiler warn you that it is wrong.
The original error seemed memory allocation related and caused me to suspect the way I used the autoreleasepool but now I am fairly convinced this has nothing to do with the issue.

Can you use both strong and retain in the same property declaration?

I've recently started working on someone else's code base and I've come across a lot of this
#property (strong, retain) TYPE *iVar;
I've never seen both Strong and Retain used in the same property declaration. I'm surprised that it even compiles, as retain already implies strong.
The project uses arc, and is a few months old so legacy isn't the problem here, the deployment target is iOS6.
Is there any valid reason why you would want to do this?
There is no reason to use property declarations with both retain and strong - according to Apple's documentation, the two are synonyms:
The keywords weak and strong are introduced as new declared property attributes, as shown in the following examples.
// The following declaration is a synonym for: #property(retain) MyClass *myObject;
#property(strong) MyClass *myObject;
If you are using ARC then just use strong.
Mixing the two may be allowed now but may produce compiler warnings / errors in the future. Not to mention it looks really odd.

Why doesn't Xcode provide autocompletion for dot-notated properties on objects of type id<protocol>?

Given this protocol definition:
#protocol MyProtocol <NSObject>
#property (nonatomic, strong) NSString *someProperty;
#end
Why will Xcode gladly offer autocompletion for this statement:
id<MyProtocol> thing = [ThingManager currentThing];
[thing someProperty]; // Xcode offered autocompletion here
But it doesn't offer autocompletion when I try to access the same property using dot-notation:
id<MyProtocol> thing = [ThingManager currentThing];
thing.someProperty; // Xcode claimed there were
// "No completions" available
// after the period
Because id is a base type, Xcode and CLANG are uneasy about providing dot-syntax access against it because dot syntax is just syntactic sugar for a method call to an associated setter or getter in a normal object, but id has no defined method members. Looking at it from the C side of things, id is a typedef for a struct pointer that the compiler cannot see the members of, which means it cannot access them (never mind the fact that you would need to dereference id before dot-access would make any semantic sense).
Back to the Objective-C side of things, protocols don't actually add methods or properties to the classes that claim to implement them, rather they serve as a specifier to other classes that an object that conforms to a given protocol implements a series of methods. As for the method-syntax being completed, Xcode pools all of the given methods of all the files imported into a given .m file because, an object of type id can receive any message*
*of course, it can receive the message, but it'll still crash if it's unimplemented.
This is kind of a tangential answer, and a thought experiment.
But before that, I'll note that you could get your property autocomplete by skipping id, like this:
NSObject<MyProtocol> *thing;
thing.▮
But assuming you don't want the entire list of NSObject methods gumming up your completion, you could do something like
EmptyClass<MyProtocol> *thing = [ThingManager currentThing];
// completion list will be (close) to only the protocol props
thing.▮
EmptyClass serves a similar "OK, no promises!" role that id does, but autocomplete likes it. Here's EmptyClass:
NS_ROOT_CLASS
#interface EmptyClass
#end
#implementation EmptyClass
+ (void)initialize {} // required
#end
Mind you, the object in thing is not actually rooted on EmptyClass (can't be), so this is high fakery. However, it
vastly underpromises what thing can actually do.
doesn't (can't!) instantiate an EmptyClass object.
So why not? If you try really hard, you can cause problems like
EmptyClass *nooooo = [[NSClassFromString(#"EmptyClass") alloc] init];
which will immediately exception. But not really a tricky bug to avoid.
A gotcha wouldn't surprise me, but I don't know one right now. Please, leave a comment if you do.

Change with properties and ivars after ARC migration

I have used the "Convert to Objective C ARC" option in Xcode 4.3 to convert a project started in Xcode 4.0 to use ARC. After fixing errors that the tool found, I went trough the process where the migration tool has removed all the release messages as well as retain attributes in my properties declarations. So now I have all of my properties having only (nonatomic) attribute. From reading the documentation I still don't have a clear answer on what to do.
So my question is: In case you omit the keyword regarding the setter semantics (strong, weak, retain, assign) in the property declaration, what is the default attribute on properties when using ARC?
I found in the documentation that the default property attribute is assign. However, they also say that now the default attribute for ivars, in case you omit it, is strong.
To better explain my question, here is an example. I header file we have the declaration:
#property (nonatomic) MyClass *objectToUse;
and in our implementation we just have
#synthesize objectToUse;
If we then write inside some method:
self.objectToUse = [[MyClass alloc] init];
have we created an strong (retain) or weak (assign) reference? If we instead write
objectToUse = [[MyClass alloc] init];
by using the ivar have we changed the situation regarding the object retention policy? It seems to me that now with ARC, the best practice of using the properties for memory management is not the same practice anymore.
I've opened an Technical Support Incident. The engineer verified that the default was changed from "assign" to "strong". The reason is exactly the inconsistency you described. Now ivars and #properties have the same default.
He said both the documentation (and warnings some people are getting) are wrong and will be fixed. (The conversion tool is correct.) Until that is done, I would avoid the implicit default altogether. Always explicitly specify "strong", "weak", or "assign".
Edit: The clang documentation now officially documents this change.
The default relationship type is still assign, i.e. a weak ref. In addition, in ARC mode the compiler will generate an error when #synthesizeing accessors unless you explicitly specify a relationship type.
The difference between assigning to self.objectToUse and objectToUse is that the second form will always use ARC to retain or assign, while the first form will use copy if you specified a copy relationship.

Objective-C properties, how do they work?

Assume we have a class Foo with a property bar.
Declared like this in the header:
#interface Foo : NSObject {
int bar;
}
#property (nonatomic, assign) int bar;
#end
In the .m I have #synthesize bar; of course.
Now, my question is, if I remove the int bar; line, the property behaves the same way, so, is that line really necessary? Am I missing some fundamental concept?
Thanks!
The "modern" Objective-C runtime generates ivars automatically if they don't already exist when it encounters#synthesize.
Advantages to skipping them:
Less duplication in your code. And the #property gives the type, name and use (a property!) of the ivar, so you're not losing anything.
Even without explicitly declaring the ivar, you can still access the ivar directly. (One old release of Xcode has a bug that prevents this, but you won't be using that version.)
There are a few caveats:
This is not supported with the "old" runtime, on 32-bit Mac OS X. (It is supported by recent versions of the iOS simulator.)
Xcode may not show autogenerated ivars in the debugger. (I believe this is a bug.)
Because of the debugger issue, at the moment I explicitly add all my ivars and flag them like this:
#interface Foo : NSObject {
#ifndef SYNTHESIZED_IVARS
int ivar;
#endif
}
#property (nonatomic, assign) int bar;
#end
My plan is to remove that block when I've confirmed the debugger is able to show the ivars. (For all I know, this has already happened.)
If there is not a instance variable (ivar) with the same name as the property the modern runtime creates a new ivar of the specified property name to hold the property value when it sees #synthesize.
If your property was not defined nonatomic and you want your code to be threadsafe it may help you to not reference the ivar (whether you declared it or it was synthesized), as that will prevent you from accessing it directly when the property is being changed. To my knowledge there is no way to acquire the same lock that is acquired by #synthesize for an atomic property and therefore you cannot perform safe reads of an atomic property's ivar other than by its synthesized accessor (unless you code an explicit setter and lock something yourself). If you are interested in writing you own accessors I have a blog post on that here.
I believe it is more usual to have an explicit ivar for each property, but this may be because most code is intended to be compatible with the legacy runtime rather than because it is inherently good practice.
Edit: I have corrected paragraph 1 to say that the synthesized ivar has the name of the property; I couldn't see any discussion of its name in Apple's docs so I had assumed it was not user accessible. Thanks to the commenters for pointing this out.
In the latest Objective-C runtime, it turns out that all ivars (in this case, bar) are dynamically added to the class from the #property/#synthesize declaration and do not need a corresponding ivar declaration in the class header. The only caveat to this is that latest Objective-C runtime which supports this feature does not include support for 32 bit applications.
This page from the excellent Cocoa with Love blog explains more.
If you are using the modern Obj-C runtime (e.g. with the LLVM compiler in Xcode 4), the compiler can automatically create the instance variable for you. In that case, you don't need to declare the ivar.
The modern runtime is supported by iOS, the iOS Simulator and 64-bit Mac OS X. You can't build your project for 32-bit OS X with the modern runtime.
I believe it only works that way in 64bit mode. If you try to build for a 32bit target, it will throw an exception.
Scott Stevenson has a good overview of the objective-c 2 changes, and mentions the 64bit runtime changes here