After multiple searches and reads about property attributes, I still can't understand them completely and create a reflex of using them correctly.
I have multiple questions:
1) What does a default
attribute mean?
As I understood, not specifying an attribute in a "group", the default one is used, so this:
@property NSString *string;
is atomic
, right?
By this logic, this article says that strong
and assign
are defaults, so if I have:
@property (nonatomic) NSString *string;
is the string
property strong
or assign
?
How are the available attributes "grouped"? Or as Xcode words this, what attributes are mutually exclusive?
2) Are there any generic rules that one should follow?
For example, I saw one comment that said that you should use copy
for classes with mutable variants like NSString
, NSArray
.
And another one that said that you should use assign
for C objects.
So, is it a good idea to always use:
@property (copy, nonatomic) NSString *string;
@property (assign, nonatomic) CGFloat float;
?
What other standard practices exist for property attributes?
3) What problems could arise if I use "wrong" attributes? What if I just use nonatomic
for all the properties in a project?
1a) The default attributes for a property are atomic
, and strong
(for an object pointer) or assign
(for a primitive type), and readwrite
. This assumes an all ARC project.
So @property NSString *string;
is the same as @property (atomic, strong, readwrite) NSString *string;
. @property int value;
is the same as @property (atomic, assign, readwrite) int value;
.
1b) Attributes are grouped as follows:
Pick one and only one from each of those three groups.
Actually, the latest Objective-C adds support for nullable/nonnull
with the default being nullable
.
2) General rules are as you say.
strong
.assign
.weak
should be used in child/parent references to avoid reference cycles. Typically the parent has a strong reference to its children and the children have a weak reference to their parent. Delegates are typically weak
for the same reason.copy
is typically used for NSString
, NSArray
, NSDictionary
, etc. to avoid issues when they are assigned the mutable variant. This avoids the problem of the value being changed unexpectedly.copy
with NSMutableString
, NSMutableArray
, etc. because when you assign the mutable value to the property, the copy
attribute results in the copy
method being called which gives back a non-mutable copy of the original value. The solution is to override the setter
method to call mutableCopy
.3) Using the wrong attribute could have serious problems depending on the needs of the property and the attribute being used.
Using assign
instead of strong
for an object pointer is probably the worst mistake. It can lead to app crashes due to trying to access deallocated objects.
Using nonatomic
instead of atomic
on a property that will be accessed concurrently on multiple threads may lead to really hard to find bugs and/or crashes.
Using strong
instead of copy
for NSString
or NSArray
(and other collections) can possibly lead to subtle and hard to find bugs if mutable variants were assigned to the property and other code later modifies those values.
@rmaddy's answer is a good one.
I would add the following.
If you are creating (or have inherited) classes that interoperate with Swift, it is very useful to include nullable
or nonnull
property attributes. If you add it in any part of a header file, you will need to specify it for all parts of the header file (compiler warnings will help you). It's even quite useful for Objective-C callers to know from the method signature what may and may not be a nil value.
Another property of note is class
. You can add a property to the class.
Adding these two items together, and if you are implementing a singleton,
+ (MyClass *)sharedInstance;
it's quite useful to define it as a property:
@property (class, nonatomic, nonnull, readonly) MyClass *sharedInstance;
(In which case you are required to add a backing variable for it as described in this article)
This will let you access the shared instance via dot notation.
[MyClass.sharedInstance showMeTheMoney:YES];
And in Swift, the rather annoying
MyClass.sharedInstance()?.showMeTheMoney(true)
turns into
MyClass.sharedInstance.showMeTheMoney(true)
‡ maybe it's just 3 characters to you, but it keeps my head from exploding mid-day.
Edit: I would add, try out
+ (instancetype)shared;
This 1) shortens the naming to concur with modern Swift convention, and 2) removes the hardcoded type value of a (MyClass *)
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With