
There are many different places where the value of the attributes used to declare properties is described, but, as a rule, all such sources either describe the use of only one of the attributes, or contain a whole bunch of references to other documents, passing on which you eventually lose track of reasoning. Of course, you need to strive to learn all the details, for which you need to read the mountains of literature. But for the beginning it is quite enough to understand the basics. Below, I have tried as simply as possible to state the main attributes used in the declaration of properties, their meaning and the main cases when it is worth using one or another attribute value.
Brief introduction
A good way to access the data of an object in object-oriented languages is to use the setter and getter methods (they are also a mutator and accessor), where the former sets the property value and the latter returns the property value at the moment instead of directly accessing instance variables. To get rid of the need to declare a bunch of methods, properties were invented that reduced the amount of necessary writing, but did not change the essence: when accessing properties, it is still caused, depending on the operation performed, a setter or getter that can be generated automatically. In fact, the attributes specified when declaring properties determine how the data access methods will be generated.
Directly, instruction
To begin with, we will divide all the attributes that the property has into groups:
')
- accessibility attributes (readonly / readwrite),
- ownership attributes (retain / strong / copy / assign / unsafe_unretained / weak),
- atomicity attribute (atomic / nonatomic).
- nullability attribute (null_unspecified / null_resettable / nullable / nonnull) - appeared in xcode 6.3
Attributes that allow you to specify the name of the setter and getter will not be considered - for them as such there are no rules, except for those that the Code Style you use provides. Explicitly or implicitly, but attributes of all types are specified for each property.
Accessibility Attributes
- readwrite - indicates that the property is available for both reading and writing, that is, both the setter and the getter will be generated. This value is set to all properties by default unless otherwise specified.
- readonly indicates that the property is read-only. This value should be used in cases when a change in the property “outside” during the execution of its task by the object is undesirable, or when the property value is not stored in any variable, but is generated based on the values of other properties. For example, the User object has the properties firstName and lastName, and for convenience, the readonly property fullName, the value of which is generated based on the values of the first two properties.
When it is undesirable to change the property “outside”, it is usually declared in the class interface as readonly, and then redefined as readwrite in the class extension, so that inside the class also change the value not directly in the variable, but through the setter .
Ownership Attributes
This is the most extensive type of attribute, largely due to the coexistence of manual and automatic memory management.
With ARC enabled, variables, like properties, have a ownership attribute, only in this case the set of values is smaller than the properties: __strong / __ weak / __ unsafe_unretained, and this applies only to data types that are subject to ARC data that do not fall under the ARC will not be considered here, so as not to complicate what is meant to be a simple cheat sheet). Therefore, when describing the values of this attribute for the properties, we will also indicate what value of the ownership attribute the corresponding instance variable should have when ARC is on (if the variable is created automatically, it is immediately created with the desired value of this attribute. If you define the variable yourself, you must manually specify her the proper property attribute value).
- retain (the corresponding variable must be with the __strong attribute) - this value indicates that in the generated setter the reference count for the object being assigned will be increased, and for the object that the property referred to before, the reference count will be reduced. This value is applicable when ARC is disabled for all Objective-C classes in all cases when no other values are appropriate. This value has been preserved since the ARC has not yet existed and, although the ARC does not prohibit its use, it is better to use strong instead of it when automatic link counting is enabled.
- strong (the corresponding variable must be with the __strong attribute) - this value is similar to retain, but applies only when automatic link counting is enabled. When using ARC, this value is used by default. Use strong in all cases that are not suitable for weak and copy, and everything will be fine.
- copy (the corresponding variable must be with the __strong attribute) —with this value of the ownership attribute in the generated setter, the corresponding instance variable is assigned the value returned by the copy message sent to the assigned object. Using this property attribute value imposes some restrictions on the class of an object:
- the class must support the NSCopying protocol,
- the class must not be mutable. Some classes have a mutable subclass, for example, NSString-NSMutableString. If your property is an instance of a “mutable” class, using copy will lead to undesirable consequences, since the copy method will return an instance of its “unbutable” relative. For example, calling copy on an instance of NSMutableString will return an instance of NSString.
Example:
@property (copy, nonatomic) NSMutableString *foo; ...
The second limitation implies the most important reason for using the copy value: all public properties that are instances of a class that has a “mutable” subclass are best created with this property attribute value. The copy method works like “retain” for “non-mutable” classes - no copying will occur, extra memory and time will not be spent, but the property will be protected from defining an instance of a “mutable” subclass. For example, in the property of type NSArray it will be impossible to set an object of class NSMutableArray, and therefore it will be impossible to change the property “outside”, bypassing the setter.
Example:
@interface Foo : NSObject … @property (copy, nonatomic) NSArray *bar; @property (strong, nonatomic) NSArray *barNotProtected; … @end … NSMutableArray *mutableArray = [@[@1, @2, @3] mutableCopy]; Foo *foo = [Foo new]; foo.bar = mutableArray; // bar foo.barNotProtected = mutableArray; // barNotProtected [mutableArray removeObjectAtIndex:0]; // foo.barNotProtected (@[@2, @3]), , foo.bar (@[@1, @2, @3]).
- weak (the corresponding variable must be with the __weak attribute) - this value is similar to assign and unsafe_unretained. The difference is that special street magic allows variables with such a property attribute value to change their value to nil when the object pointed to by the variable is destroyed, which very well affects the stability of the application (for, as you know, nil responds to any messages , so no EXC_BAD_ACCESS to you when referring to an object that has already been deleted). This property attribute value should be used when ARC is enabled to exclude the retain cycle for properties that store a pointer to an object delegate and the like. This is the only property attribute value that is not supported when ARC is turned off (as with ARC enabled on iOS up to version 5.0).
- unsafe_unretained (the corresponding variable must be with the __unsafe_unretained attribute) - a property with this type of ownership simply stores the address of the object assigned to it. Methods for accessing such a property do not affect the object's reference count. He can retire, and then accessing such a property will lead to crash (and therefore unsafe). This value was used instead of weak, when ARC already appeared, but it was necessary to still support iOS 4.3. Now its use can be justified except by the speed of work (there is information that the magic of weak properties requires considerable time, although, of course, with the naked eye with the current performance you will not notice), therefore, especially at first, you should not use it.
- assign (the corresponding variable should be with the __unsafe_unretained attribute, but since there are ownership attributes for only ARC types with which it is better to use strong or weak, you hardly need this value) - just assign an address. Without ARC, is the default value of the ownership attribute. It should be applied to properties of types not covered by ARC (these include primitive types and so-called non-object types (non-object types) like CFTypeRef). Without ARC, it is also used instead of weak to exclude retain cycles for properties that store a pointer to an object delegate and the like.
Atomicity attribute
- atomic is the default value for this attribute. It means that the accessor and the mutator will be generated in such a way that when they are accessed simultaneously from different threads, they will not be executed simultaneously (i.e., anyway, first one thread will do its job - will set or get the value, and only after that will another thread can do the same). Due to the implementation features of properties with such an attribute of atomicity, you cannot redefine just one of the access methods (if you override, override both, and freeze yourself with protection from simultaneous execution in different threads). Do not confuse the atomicity of the property with the thread safety of the object. For example, if you get the value of the name and surname from the properties of an object in one stream, and change the same values in the other, it may well turn out that the value of the name you get is old, and the surnames are already changed. Accordingly, it is necessary to apply for the properties of objects that can be accessed from many threads simultaneously, and you need to be saved from getting any invalid values, but if you need to ensure full thread safety, this alone may not be enough. Among other things, it is worth remembering that the access methods of such properties are slower than nonatomic, which, of course, little things in the scale of the universe, but still protects the ruble, so where there is no need, it is better to use nonatomic.
- nonatomic - the opposite value is atomic - for properties with this attribute value of atomicity, access methods are not burdened with protection against simultaneous execution in different threads, therefore, they are executed faster. This value is suitable for most properties, since most of the objects are still used only in one thread, and there is no sense in “loading” them with unnecessary features. In general, for all properties for which you cannot explain why it should be atomic, use nonatomic, and you will be fast and easy and smart and just some kind of holiday.
Nullability attribute
This attribute does not affect the generated access methods. It is intended to indicate whether a given property can take the value nil or NULL. Xcode uses this information when interacting with Swift code with your class. In addition, this value is used to output warnings in case your code does something contrary to the stated behavior. For example, if you are trying to set a nil property that is declared nonnull. And most importantly, this information will be useful to someone who will read your code. There are a couple of restrictions on using this attribute:
- It cannot be used for primitive types (they do not need it, since in any case they do not take the values nil and NULL)
- cannot be used for multi-level pointers (for example, id * or NSError **)
- null_unspecified - is used by default and says nothing about whether a property can take the value nil / NULL or not. Before the appearance of this attribute, this is exactly how we perceived absolutely all properties. This is bad in terms of pithy headings, so using this value is not recommended.
- null_resettable - this value indicates that the getter of such a property will never return nil / NULL due to the fact that when setting such a value, in fact, some default property will be assigned to the property. And since the generated access methods do not depend on the value of this attribute, you yourself will either have to override the setter so that when you receive nil / NULL input, it will save the default value in ivar, or override the getter so that it returns the default value if the corresponding ivar == nil / NULL. Accordingly, if your property has a default value, declare it as null_resettable.
- nonnull - this value indicates that a property marked with this attribute will not take the value nil / NULL. In fact, you can still get nil if, for example, you try to get the value of this property from nil and simply because Xcode is not very strict on this. But it will already be your mistake and Xcode will point out to you as much as possible in its warnings. Use it if you are sure that the value of this property will never be nil / NULL and you want the IDE to help you keep track of it.
- nullable - this value indicates that the property can be nil / NULL. It, like null_unspecified, does not oblige to anything, but due to its greater certainty, among these two it is more appropriate to use nullable. Thus, if neither null_resettable nor nonnull suits you, use nullable.
For added convenience, you can change the default value from null_unspecified to nonnull for a specific block of code. For this you need to put NS_ASSUME_NONNULL_BEGIN before such a block and NS_ASSUME_NONNULL_END - after it.
At this story I hasten to finish. Who has something to add or argue with - welcome in comments.
Upd: Thanks to
storoj, the setter example was corrected in the retain paragraph, and the atomic paragraph was slightly refined. Thanks for the comments.
Upd2: Thanks to the convincing
fsmorygo , a section on the nullability attribute has
been added