Objective-C property attributes best practices
@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 *)
.
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:
- atomic/nonatomic
- strong/weak/assign/copy
- readwrite/readonly
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.
- Object pointers should usually be
strong
. - Primitive types should be
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 typicallyweak
for the same reason.copy
is typically used forNSString
,NSArray
,NSDictionary
, etc. to avoid issues when they are assigned the mutable variant. This avoids the problem of the value being changed unexpectedly.- There's a big "gotcha" using
copy
withNSMutableString
,NSMutableArray
, etc. because when you assign the mutable value to the property, thecopy
attribute results in thecopy
method being called which gives back a non-mutable copy of the original value. The solution is to override thesetter
method to callmutableCopy
.
3) Using the wrong attribute could have serious problems depending on the needs of the property and the attribute being used.
Using
assign
instead ofstrong
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 ofatomic
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 ofcopy
forNSString
orNSArray
(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.