The problem
Let’s start with a few questions:
- What does the system do for us when we define @property?
- Can we define at sign property in the classification?
- 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:
- The property _property is generated
- It declares getters and setters
- 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
- AssociationsManager
- AssociationsHashMap
- ObjectAssociationMap
- 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.