Welcome to pay attention to the public number: good code farming, attention to prepare some small gifts for you, for you in the career path to help!

The background,

Since the underlying structure of the classification cannot add member variables, the effect of member variables can be implemented using runtime associated objects


Member variables cannot be added to a class

  • One thing to note is that classification can declare attributes, but cannot add member variables, which can be confusing.If you declare a property, doesn't it automatically generate a member variable?However, this is not the case in the classification. In the declaration of attributes in the classification, there is no automatic addition of member variables, and there is no automatic implementation of set and GET methods.Add member variables and implement set and get methodsAdding member variables to categories raises the error that Instance variables may not be placed in categories

  • Classification underlying 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);
};
Copy the code

As you can see from the underlying structure of classification, there is a place for instance methods, class methods, protocols, and attributes, but there is no place for member variables, so the underlying structure determines that member variables cannot be added.


The runtime associated object implements the effect of classifying and adding member variables

  • Associated object API adds associated objects
void objc_setAssociatedObject(id object, const void * key,
                                    id value, objc_AssociationPolicy policy)
Copy the code

The first parameter: fill in the object for which the associated object is to be added (usually fill in self). The second parameter: the key of the associated object, and then obtain the associated object according to this key. The third parameter: the value of the associated object

typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {

OBJC_ASSOCIATION_ASSIGN = 0, // assign

OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, // strong, nonatomic

OBJC_ASSOCIATION_COPY_NONATOMIC = 3, // copy, nonatomic

OBJC_ASSOCIATION_RETAIN = 01401, // strong, atomic

OBJC_ASSOCIATION_COPY = 01403 // copy, atomic

};

  • Getting associated Objects
id objc_getAssociatedObject(id object, const void * key)
Copy the code

The first argument: the object to be associated with the second argument: the key of the associated object, as when adding the associated object

  • Removing associated Objects
void objc_removeAssociatedObjects(id object)
Copy the code

Parameter: The object to be associated with

  • #import

- (void)setName:(NSString *)name
{
    objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY);
}

- (NSString *)name
{
    return objc_getAssociatedObject(self, @selector(name));
}
Copy the code

This will do the trick of adding member variables to the classification


4. Principle of associated objects

  • Where are associated objects stored? Associated objects are not stored in the same class as member variables of the same class. Because there is no place for them in the classification hierarchy, associated objects are stored in the global AssociationsManager.
  • AssociationsManager has an AssociationsHashMap that acts like a dictionary
class AssociationsManager { // associative references: object pointer -> PtrPtrHashMap. static AssociationsHashMap *_map; public: AssociationsManager() { AssociationsManagerLock.lock(); } ~AssociationsManager() { AssociationsManagerLock.unlock(); } AssociationsHashMap &associations() { if (_map == NULL) _map = new AssociationsHashMap(); return *_map; }};Copy the code
  • AssociationsHashMap takes the object to be associated with as the key(a parameter of the associated object), saves all associated attributes of the object, and value is ObjectAssociationMap
class AssociationsHashMap : public unordered_map<disguised_ptr_t, ObjectAssociationMap *, DisguisedPointerHash, DisguisedPointerEqual, AssociationsHashMapAllocator> { public: void *operator new(size_t n) { return ::malloc(n); } void operator delete(void *ptr) { ::free(ptr); }};Copy the code
  • ObjectAssociationMap has added the key value of the associated object as the key value. The value is objcasSociety, which contains the value of the associated object and the association policy
class ObjectAssociationMap : public std::map<void *, ObjcAssociation, ObjectPointerLess, ObjectAssociationMapAllocator>
Copy the code