The problem

Let’s start with a few questions:

  1. What does the system do for us when we define @property?
  2. Can we define at sign property in the classification?
  3. If not, how do you make it happen?

@property

What did you do when you defined @property

To answer the first question, to define @property, the system does the following for us:

  1. The property _property is generated
  2. It declares getters and setters
  3. Getters and setters are implemented

Whether you can define at sign property in a Category

We could try to define @property in a class and see that getters and setters are only declared but not implemented, and the member variable _property is not declared either.

So we can try to declare _property ourselves and override the getter and setter, and if we can’t declare _property, we’ll get an error.

So it follows that you can’t define @property in a Category

The structure of the body

Let’s look at the category_T structure

struct category_t { const char *name; classref_t cls; struct method_list_t *instanceMethods; struct method_list_t *classMethods; struct protocol_list_t *protocols; struct property_list_t *instanceProperties; // Fields below this point are not always present on disk. struct property_list_t *_classProperties; method_list_t *methodsForMeta(bool isMeta) { if (isMeta) return classMethods; else return instanceMethods; } property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi); protocol_list_t *protocolsForMeta(bool isMeta) { if (isMeta) return nullptr; else return protocols; }};Copy the code

Obviously, there is no place to store member variables.

Getters & Setters

If we define an @property and then directly assign or evaluate it, it will crash.

The reason for the crash is unrecognized selector, so you can see that the get and set methods are not implemented.

associations

usage

If you need to use @property in a class, the simplest way is to use an associative object. The general usage is as follows

- (NSString *)personParam {
    return objc_getAssociatedObject(self, _cmd);
}
 
- (void)setPersonParam:(NSString *)personParam {
    objc_setAssociatedObject(self, @selector(personParam), personParam, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
Copy the code

Next, we still start with the source code and analyze the principle

Principle of set method

Objc_setAssociatedObject (ID object, const void *key, ID value, objc_AssociationPolicy policy) _object_set_associative_reference(const void *key, ID value, uintptr_t policy)

This function. Let’s click inside to find out.

Void _object_set_associative_reference(id object, const void *key, ID value, uintptr_t policy) { object && ! value) return; if (object->getIsa()->forbidsAssociatedObjects()) _objc_fatal("objc_setAssociatedObject called on instance (%p) of class  %s which does not allow associated objects", object, object_getClassName(object)); DisguisedPtr<objc_object> backup {(objc_object *)object}; ObjcAssociation association{policy, value}; Retain the new value (if any) outside the lock. // Call retain or copy etc. Association.acquirevalue (); { AssociationsManager manager; AssociationsHashMap &associations(manager.get()); if (value) { auto refs_result = associations.try_emplace(disguised, ObjectAssociationMap{}); if (refs_result.second) { /* it's the first association we make */ object->setHasAssociatedObjects(); } /* establish or replace the association */ auto &refs = refs_result.first->second; auto result = refs.try_emplace(key, std::move(association)); if (! result.second) { association.swap(result.first->second); } } else { auto refs_it = associations.find(disguised); if (refs_it ! = associations.end()) { auto &refs = refs_it->second; auto it = refs.find(key); if (it ! = refs.end()) { association.swap(it->second); refs.erase(it); if (refs.size() == 0) { associations.erase(refs_it); } } } } } // release the old value (outside of the lock). association.releaseHeldValue(); }Copy the code

As you can see, the structure for storing associated objects should be a Map.

The code here is more complex, we can change the way of thinking, first some core classes and structures for a parsing. We can find several key classes

  1. AssociationsManager
  2. AssociationsHashMap
  3. ObjectAssociationMap
  4. ObjcAssociation

AssociationsManager

We entered the class by declaring a variable manager of type AssociationsManager and calling the get() method

class AssociationsManager { using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>; static Storage _mapStorage; Public: / / here you can see, in the object, within the limits of life cycle is to add the lock AssociationsManager () {AssociationsManagerLock. The lock (); } ~AssociationsManager() { AssociationsManagerLock.unlock(); AssociationsHashMap &get() {return _mapstorage.get (); } static void init() { _mapStorage.init(); }};Copy the code

AssociationsHashMap

typedef DenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap> AssociationsHashMap;
Copy the code

ObjcAssociation

There are more implementations of this class, so let’s look at some of them first


class ObjcAssociation {
    uintptr_t _policy;
    id _value;
public:
 
};
Copy the code

The two values are familiar: the policy and value passed in when we call the Runtime API.

General structure

At this point, we can comb through the general structure:




Principle of GET method

Objc_getAssociatedObject (id object, const void *key), The function id _object_get_associative_reference(id object, const void *key) is called.

id _object_get_associative_reference(id object, const void *key) { ObjcAssociation association{}; {// AssociationsManager manager; AssicuationsHashMap AssociationsHashMap & Associations (manager.get())); // Use the iterator here, Find the object corresponding to the first ObjectAssociationMap AssociationsHashMap: : iterator I = associations. The find ((objc_object *) object); if (i ! = associations.end()) { ObjectAssociationMap &refs = i->second; ObjectAssociationMap::iterator j = refs.find(key); if (j ! = refs.end()) { association = j->second; association.retainReturnedValue(); } } } return association.autoreleaseReturnedValue(); }Copy the code

It’s very simple to see that it takes the final value layer by layer and then returns it.

Write in the back

So far, the Category series has been written, and I believe you have a deep understanding of the categories. But I hope you can use more, only practice can be transformed into their own knowledge.

Welcome to point out any mistakes in this article.