The interview questions

Q: Can you add member variables to a Category? If so, how do YOU add member variables to a Category?

A: You cannot add a member variable directly, but you can add a member variable indirectly through the Runtime.

RunTime is the Category dynamically associated object

To add attributes to a class in a system using the RunTime, you first need to understand the relationship between objects and attributes. We learned that when an object is initially initialized it has a property of nil, and assigning a value to a property is essentially referring to a block of memory where the content is stored, so that the properties of this object are associated with that block of memory.

So if you want to add attributes dynamically, you’re actually creating some kind of association dynamically. The only way to add attributes to a class in the system is through classification.

So here we add a name property to NSObject, to create a class of NSObject and we can add properties to classes using @property

@property(nonatomic,strong)NSString *name;
Copy the code

throughExplore the nature of categoriesWe know that even though we can add attributes to a class with @property, it doesn’t automatically generate private attributes, it doesn’t automatically generate set, so the implementation of get, it just generates the declaration of set,get, and we need to implement it ourselves.

Method one: We can add attributes to a class by using static global variables
static NSString *_name;
-(void)setName:(NSString *)name
{
    _name = name;
}
-(NSString *)name
{
    return _name;
}
Copy the code

However, the _name static global variable is not associated with the class. Regardless of whether the object is created or destroyed, the _name variable exists as long as the program is running. It is not a real property.

Method 2: Use RunTime to dynamically add attributes

The RunTime provides methods for dynamically adding and obtaining properties.

-(void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, @"name",name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
-(NSString *)name
{
    return objc_getAssociatedObject(self, @"name");    
}
Copy the code
  1. Dynamically adding properties
objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy);
Copy the code

Parameter 1: ID object: To add an attribute to an object, in this case to add an attribute to yourself, use self. Void * == ID Key: specifies the property name. The value of the property of the associated object is obtained by key. In **objc_getAssociatedObject, the value of the property is obtained by secondary key and returned. Parameter 3: id value** : the associated value, that is, the value passed by the set method to the attribute to save. Parameter 4: objc_AssociationPolicy Policy: indicates the policy in which attributes are saved. There are several

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { OBJC_ASSOCIATION_ASSIGN = 0, // Specify a weak reference to the associated object OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // specify a strong reference to the associated object, Non-atomic OBJC_ASSOCIATION_COPY_NONATOMIC = 3, // specifies that the associated object is copied, non-atomic OBJC_ASSOCIATION_RETAIN = 01401, // Specifies a strong reference to the associated object, Atomicity OBJC_ASSOCIATION_COPY = 01403 // Specifies that the associated object is copied, atomicity};Copy the code

The key just needs to be a pointer, and we can pass in at sign selector name.

  1. Get properties
objc_getAssociatedObject(id object, const void *key);
Copy the code

Parameter 1: ID object: Gets the associated attribute in which object. Void * == ID Key: an attribute corresponding to the key in **objc_setAssociatedObject**.

  1. Remove all associated objects
- (void)removeAssociatedObjects {// Remove all associated objects objc_removeAssociatedObjects(self); }Copy the code

At this point, the name attribute has been successfully added to NSObject, and the NSObject object can assign a value to the attribute using point syntax.

NSObject *objc = [[NSObject alloc]init];
objc.name = @"xx_cc";
NSLog(@"% @",objc.name);
Copy the code

As you can see, associative objects are very simple to use, so let’s explore the underlying principles of associative objects

The implementation principle of associated objects

The core objects that implement associative object technology are

  1. AssociationsManager
  2. AssociationsHashMap
  3. ObjectAssociationMap
  4. Objcassociety where Map is similar to the dictionary we normally use. Use key-value to map the stored value one by one.

With a general awareness of the core objects of the associative object technology, we explore the forms and functions of these objects through source code

Objc_setAssociatedObject function

Go to the Runtime source code, first go to the objc_setAssociatedObject function and see its implementation

We see that the _object_set_associative_reference function is actually called internally, and we go to the _object_set_associative_reference function

_object_set_associative_reference function

AssociationsManager

Through the internal source code of AssociationsManager, it is found that there is an AssociationsHashMap object in AssociationsManager.

AssociationsHashMap

Let’s take a look at the source code inside AssociationsHashMap.

AssociationsHashMap inherits from Unordered_map by using AssociationsHashMap internal source code

From the source code of Unordered_MAP, we can see that _Key and _Tp, that is, the first two parameters correspond to the Key and Value in the map. Compared with the above source code in AssociationsHashMap, we can find that the disguised_ptr_T is passed in _Key. The value passed in _Tp is ObjectAssociationMap*.

Then we come to ObjectAssociationMap. In the figure above, ObjectAssociationMap has been marked. We find that ObjectAssociationMap also stores object Association in the form of key and Value.

And then we go to the ObjC Society

We see that objcasSociety stores _policy and _value, which we can see are the values passed in by calling objc_setAssociatedObject, That is, the value and policy that we pass in calling objc_setAssociatedObject are ultimately stored in the ObjcasSociety.

Now that we have a simple understanding of the relationship between AssociationsManager, AssociationsHashMap, ObjectAssociationMap, and ObjcasSociety, let’s read the source code carefully. Take a look at what the four parameters passed in the objc_setAssociatedObject function do on which object.

Return to the implementation of the _object_set_associative_reference function

AcquireValue (new_value) acquireValue (new_value) ¶ AcquireValue internally returns different values based on policy judgments

Then create AssociationsManager Manager; And get the AssociationsHashMap within the manager. Then we see that the first parameter object object we passed in is transformed into disguised_ptr_T type DISGUised_object through the DISGUISE function.

The DISGUISE function simply performs a bit operation on object

Then we see that the value that was treated as new_value, the same policy is stored in the Object Society. And the object society key that we pass in is stored in the ObjectAssociationMap. Disguised_object and ObjectAssociationMap are stored in associations in the form of key-value, that is, AssociationsHashMap.

If we set value to nil then the following code will be executed

As you can see from the code above, if we set value to nil, the associated object is removed from the ObjectAssociationMap.

Finally, we can clearly understand the relationship through a picture

From the above figure, we can conclude as follows: An instance object corresponds to an ObjectAssociationMap, which stores the keys and ObjcAssociation of multiple associated objects of this instance object. Stores the value and policy policies of the associated objects in the Object Society.

From this we can know that the associated object is not placed in the original object, but maintains a global map to store each object and its corresponding associated attribute table.

Objc_getAssociatedObject function

Objc_getAssociatedObject internally calls _object_get_associative_reference

_object_get_associative_reference function

From the inside of the _object_get_associative_reference function, we can see that the value is retrieved layer by layer in reverse as in the set method and then returned.

Objc_removeAssociatedObjects function

Objc_removeAssociatedObjects is used to remove all associated objects. The objc_removeAssociatedObjects function internally calls _object_remove_assocations

_object_remove_assocations function

The _object_remove_assocations function removes an object from all of its associated objects.

Conclusion:

The associated object is not stored in the memory of the associated object itself, but stored in a global unified AssociationsManager. If the associated object is set to nil, it is equivalent to removing the associated object.

At this point we are going back to the objc_AssociationPolicy policy parameter: the policy for storing attributes.

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) { OBJC_ASSOCIATION_ASSIGN = 0, // Specify a weak reference to the associated object OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // specify a strong reference to the associated object, Non-atomic OBJC_ASSOCIATION_COPY_NONATOMIC = 3, // specifies that the associated object is copied, non-atomic OBJC_ASSOCIATION_RETAIN = 01401, // Specifies a strong reference to the associated object, Atomicity OBJC_ASSOCIATION_COPY = 01403 // Specifies that the associated object is copied, atomicity};Copy the code

We can see that there is only RETAIN and COPY, but why not weak? In the face of the source code analysis, we know that the object is transformed into disguised_Ptr_T type DISGUised_object through the DISGUISE function.

disguised_ptr_t disguised_object = DISGUISE(object);
Copy the code

At the same time, we know that the weak attribute will be destroyed when there is no object and the pointer is set nil. So after the destruction of the object, although there is an AssociationsHashMap corresponding to the value object in the map, the address of object has been set nil, It causes bad address access and cannot be transformed into disguised_object according to the object object address.


This article is the summary of the underlying principle of learning, if there is any wrong place please correct, welcome everyone to exchange learning XX_CC.