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

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.

Related

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

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.

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.

Including ARC headers in non-ARC project

I made a static library that was coded using ARC. I plan to distribute this library for other people to use. I understand that nothing needs to be done to include an ARC static library in a non-ARC project, but what about including ARC header files? For example, the headers for my ARC static library declare properties as weak and strong but when I try to include these headers in a non-ARC project, the compiler freaks out.
Any ideas?
For strong, you can just use retain. They're identical.
weak is trickier, and while I know several ways that should work, I'm not certain the best way to handle it.
First, make sure you actually need it. If you're supporting iOS4, you can't have weak anyway, so the issue is moot. My gut feeling is that I'd probably just avoid weak and make all these problems go away. weak is nice, but it's not that big a deal to avoid in most cases.
That said, there are some approaches that will work. The best is probably to declare weak accessors without properties in the header. Instead of this:
#property (nonatomic, readwrite, weak) id delegate;
Do this:
- (id)delegate;
- (void)setDelegate:(id)aDelegate;
Then you can still declare a weak property inside your implementation file. The caller can still use dot notation for this, BTW.
It's possible that you'll get a compile error here, since setDelegate: technically takes a __strong id. If that's the case, just hand-implement setDelegate:
- (void)setDelegate:(id)aDelegate {
_delegate = aDelegate;
}
Haven't tested it, but that should work. You might also declare the ivar _delegate to be __weak in the #implementation block rather than declaring it as a weak property.
Like I said; I haven't tested any of this out. Please post your findings if it works.

Questions about a readonly #property in ARC

In my interface (.h) file, I have
#property(readonly) NSString *foo;
and in my implementation (.m) file, I have
#synthesize foo;
With ARC turned on, the compiler gives me this error: Automatic Reference Counting Issue: ARC forbids synthesizing a property of an Objective-C object with unspecified ownership or storage attribute.
The error goes away if I add a strong, weak, or copy to the property. Why is this? Why would there be any differences between these things for a read-only property, what are those differences, and why does the programmer have to worry about them? Why can’t the compiler intelligently deduce a default setting for a read-only property?
Another question while I’m at it: strong, weak, or copy are the only things that make sense in ARC, right? I shouldn’t be using retain and assign anymore, should I?
You've declared a #property that doesn't have a backing ivar. Thus, when the compiler sees #synthesize, it tries to synthesize a backing ivar for you. But you haven't specified what kind of ivar you want. Should it be __strong? __weak? __unsafe_unretained? Originally, the default storage attribute for properties was assign, which is the same as __unsafe_unretained. Under ARC, though, that's almost always the wrong choice. So rather than synthesizing an unsafe ivar, they require you to specify what kind of ivar you want.
With the latest version of Xcode and recent clang compilers, this error no longer occurs. You can specify a property as #property(nonatomic, readonly) NSObject *myProperty; in the interface, synthesize it in the implementation, and the resulting ivar is assumed to be strong. If you want to be explicit or choose weak, you can do so in the original property, like #property(nonatomic, readonly, retain). Objective-C is incrementally becoming less redundant.
It is here as a declaration to the rest of your code.
When you a access the property of this class from an other part of your code, you need to know if the object you get is strong or weak.
It used to be more obvious when ARC didn't exists, because the programmer needed this information. Now, ARC makes a lot of things transparent, so it is true, you might wonder why it is still here.
Why can’t the compiler intelligently deduce a default setting for a read-only property?
I assume it would be pretty easy to set the convention that no keyword means strong or means weak. If it hasn't been done, they surely had a reason.

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