📜 ⬆️ ⬇️

Property Attributes in Objective-C. Instructions for beginners

image

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:
')
  1. accessibility attributes (readonly / readwrite),
  2. ownership attributes (retain / strong / copy / assign / unsafe_unretained / weak),
  3. atomicity attribute (atomic / nonatomic).
  4. 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



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).


//        //       ARC -(void)setFoo(Foo *)foo { if (_foo != foo) { Foo *oldValue = _foo; //            ivar _foo = [foo retain]; //    ,      [oldValue release]; } } 



  1. the class must support the NSCopying protocol,
  2. 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; ... //      - (void)setFoo:(NSMutableString)foo { _foo = [foo copy]; // copy  NSMutableString    NSString,       ,      ,   ,  ,       } 

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; … @endNSMutableArray *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]). 




Atomicity attribute



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:
  1. 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)
  2. cannot be used for multi-level pointers (for example, id * or NSError **)


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.
 //    @property (copy, nonatomic, nonnull) NSString *foo; @property (copy, nonatomic, nonnull) NSString *str; @property (strong, nonatomic, nullable) NSNumber *bar; @property (copy, nonatomic, null_resettable) NSString *baz; //   NS_ASSUME_NONNULL_BEGIN @property (copy, nonatomic) NSString *foo; @property (copy, nonatomic) NSString *str; @property (strong, nonatomic, nullable) NSNumber *bar; @property (copy, nonatomic, null_resettable) NSString *baz; NS_ASSUME_NONNULL_END 


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

Source: https://habr.com/ru/post/265175/


All Articles