directory

1. Load method analysis

2. Difference between +load and +initialize

3. “Add Member Variables” to Category

4. Implementation principle of associated objects


+ Load method analysis:

  • When to call: The load method is called when the Runtime loads classes and classes. Because all classes and classes are loaded into memory at startup, all classes and classes are called once, whether they are used or not.

Question 1:

  • In the absence of inheritance, the load methods are executed in the order in which the class was compiled;
  • If you have an inheritance relationship, the load method of the parent class and the child class, no matter how you change the compile order, the load method of the parent class always executes first, okay?

Person is the parent of student, student is compiled before Person, and Person calls load first

Question 2: the article has analyzed, loaded at runtime will combine all the way to the array, classification methods always in front of the class methods, if you have the same method, the compiler will only call the final classification method, but for + load method, testing found all the classification and call the load method, is this why?

Here we still source analysis: Objc source download address mainly view source files and methods

Objc-os. mm file (mainly initialization) _objc_init objc-runtime-new.mm file load_images // load method // prepare_load_methods before loading load method Schedule_class_load (); // load call_load_methods // load all load call_class_loads Call_category_loads loads for all classes call_category_loads loads for all classesCopy the code

Load_images ->prepare_load_methods ->schedule_class_load ->add_class_to_loadable_list

(loadable_classes) (loadable_classes) (loadable_classes) (loadable_classes) (loadable_classes) (loadable_classes) (loadable_classes) (loadable_classes) Classes without inheritance, in compile order; If there is an inheritance relationship, the parent class comes before the child class.

And then we’ll go first

Analysis of question 2:

Method view order

load_images ->call_load_methods ->call_class_loads ->call_category_loads

From the above source analysis we can know:

  1. The load method of all classes is called before the load method of the class is called
  2. Each class or class has a pointer to the load method, which is called directly from the pointer and is not merged into a list of methods as other methods are. Therefore, all classes and classes execute the load method without being overridden.
  3. Loadable_classes iterates through the load methods of the class in order: the first compiled class calls load first, and the parent class loads first before the subclass loads

This method is executed when the included libraries are loaded into the system, and this process is usually performed when the program is started.

Difference between +load and +initialize

  1. The +load method for all classes and classes is called once when the program starts, while the + Initialize method is called only once when the class first receives a message.

  2. +load is called directly by calling the function address, so there is no “overwrite” problem, every class/class will be called; +initialize is called from objc_msgSend, so as with the class method above, it is implemented through the ISA pointer lookup method. All methods are merged into a list of methods, and there will be “overwrites” :

A. If the subclass does not implement +initialize, +initialize of the parent class will be called (so +initialize of the parent class may be called multiple times); B. If the class implements +initialize, override the +initialize call of the class itself.


Add Member Variables to a Category

  • In general, member variables cannot be added to a category due to the underlying structure of the category, but can be implemented through the Runtime API associated objects.

There are basically three apis

// Add the associated object

/** Parameter meanings are as follows: id object: indicates the associated object, which is usually passed by self const void *key: obtains the index key of the associated. Id value: indicates the associated objc_AssociationPolicy Policy: Association policy OBJC_ASSOCIATION_ASSIGN, = ASSIGN OBJC_ASSOCIATION_RETAIN_NONATOMIC, = nonatomic, strong OBJC_ASSOCIATION_COPY_NONATOMIC, = nonatomic, copy OBJC_ASSOCIATION_RETAIN atomic, = strong OBJC_ASSOCIATION_COPY atomic, = copy **/
void objc_setAssociatedObject(id object, const void * key,
                                id value, objc_AssociationPolicy policy)
                                
// Get the associated object
id objc_getAssociatedObject(id object, const void * key)

// Remove all associated objects
void objc_removeAssociatedObjects(id object)
Copy the code

Add a name attribute to a category as follows. Property does not implement set and GET methods in a category.

.h
@interface Person (Test)
@property(nonatomic,strong)NSString * name;
@end

.m
#import <objc/runtime.h>
@implementation Person (Test)
-(void)setName:(NSString *)name{
    //const void * key passes @selector(name) directly to ensure uniqueness
    objc_setAssociatedObject(self.@selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
-(NSString *)name{
     //_cmd represents the selector of the current method, that is, @selector(name)
    return objc_getAssociatedObject(self,_cmd);
}
@end
Copy the code

The implementation principle of associated objects

The core objects that implement associative object technology are

  • AssociationsManager
  • AssociationsHashMap
  • ObjectAssociationMap
  • ObjcAssociation

1.AssociationsManager structure source code is as follows:

spinlock_t AssociationsManagerLock;

class AssociationsManager {
    using Storage = ExplicitInitDenseMap<DisguisedPtr<objc_object>, ObjectAssociationMap>;
    static Storage _mapStorage;

public:
    AssociationsManager()   { AssociationsManagerLock.lock(); }
    ~AssociationsManager()  { AssociationsManagerLock.unlock(); }

    AssociationsHashMap &get() {
        return _mapStorage.get();
    }

    static void init(a) {
        _mapStorage.init();
    }
};
Copy the code

Main functions:

  • It maintains a singleton of spinlock_t and AssociationsHashMap, calling lock() when it is initialized, unlock() when it is destruct, and get to get a global AssociationsHashMap singleton.
  • Simply put: The AssociationsManager ensures thread-safe operations on the AssociationsHashMap by holding a spinlock_t, meaning that only one thread will operate on the AssociationsHashMap at a time.

2. Structure of AssociationsHashMap: HashMap is equivalent to NSDictionary in OC. AssociationsHashMap is defined as follows:

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

AssociationsHashMap inherits from unordered_map and uses C++ syntax. Its function is to save the mapping from the object’s DISGUised_ptr_T to ObjectAssociationMap. AssociationsHashMap stores several ObjectAssociationMaps in the form of key-value.

2.ObjectAssociationMap:

class ObjectAssociationMap : public std: :map<void *, ObjcAssociation.ObjectPointerLess.ObjectAssociationMapAllocator> {
public:
    void *operator new(size_t n) { return ::malloc(n); }
    void operator delete(void *ptr) { ::free(ptr); }};Copy the code

ObjectAssociationMap stores the mapping from key to Objcasassociation. We can understand that ObjectAssociationMap stores several Objcasassociation objects in key-value form. This data structure holds all associated objects corresponding to the current object;

3. Objcassociety: ObjcAssociation is defined as follows:

class ObjcAssociation {
    uintptr_t _policy;
    id _value;
public:
    ObjcAssociation(uintptr_t policy, id value) : _policy(policy), _value(value) {}
    ObjcAssociation() : _policy(0), _value(nil) {}
    
    uintptr_t policy() const { return _policy; }
    id value() const { return _value; }
    
    bool hasValue() { return _value ! = nil; }};Copy the code

The ObjcAssociation object holds its associated objects. The _policy and _value fields hold the policy and value passed in when we use objc_setAssociatedObject.

The overall relationship is as follows:

Conclusion:

  1. The associated object is not stored in the memory of the associated object itself, but stored in a global unified AssociationsManager
  2. The AssociationsHashMap is globally unique and managed by the AssociationsManager.
  3. ObjectAssociationMap A data structure (ObjectAssociation object) that stores associated objects using keys. Each object corresponds to an ObjectAssociationMap. ObjectAssociationMap holds the added associated objects, which are mapped by key-value.
  4. Adding attributes to a class by associating it with an object actually helps it implement the set and GET methods, not add an attribute.