iOS - Classification, load, initialize


It's a little messy

classification

Category classification is a language feature added in Objective-C 2.0. Its main function is to add methods to existing classes. Category can add new methods to the original class without subclassing or invading the source code of a class, so as to achieve the purpose of extending a class or separating a class.

Generally, Category has the following usage scenarios:

  • Separate the different implementation methods of the class into different files
  • Multiple developers can work together to complete a class
  • Declare private method
  • Simulate multiple inheritance
  • Expose framework private methods

Classification structure

struct _category_t {
	const char *name;	        //The name of the class to which the classification belongs
	struct _class_t *cls;
	const struct _method_list_t *instance_methods;   //Example method
	const struct _method_list_t *class_methods;   //Class method
	const struct _protocol_list_t *protocols;    //agreement
	const struct _prop_list_t *properties;       //attribute
};

Turn the newly created classified. m file into c + +, and you can see the underlying structure of the above classification_ category_t structure

_ category_ The T structure defines a structure variable
The variable name is a little long_ OBJC_$_CATEGORY_Person_$_Test

static struct _category_t _OBJC_$_CATEGORY_Person_$_Test __attribute__ ((used, section ("__DATA,__objc_const"))) = 
{
	"Person",
	0, // &OBJC_CLASS_$_Person,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Test,
	(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Test,
	(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_Person_$_Test,
	(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_Test,
};

The back is right_ category_t structure type variable_ category_t OBJCKaTeX parse error: Expected group after '_' at position 17: …CATEGORY_Person_ ̲_ Test assignment.
Definition of comparative classification structure

  • The first member, "Person", is the class name to which the classification belongs
  • The second is set to 0
  • Third member, (const struct _method_list_t *)&_ OBJC_$_ CATEGORY_ INSTANCE_ METHODS_ Person_$_ Test
    This is also a structure variable, which stores the instance method, as shown in the figure

    Implementation of instance method test
  • The fourth member, (const struct _method_list_t *)&_ OBJC_$_ CATEGORY_ CLASS_ METHODS_ Person_$_ Test
    It is also a structural variable that stores class methods, as shown in the figure

    Implementation of class method test1

    In classification, if object methods and class methods are implemented, they will be added to the corresponding method list structure.
    But if it is only defined and not implemented, it will not be added
  • The fifth member, (const struct _protocol_list_t *)&_ OBJC_ CATEGORY_ PROTOCOLS_$_ Person_$_ Test
    For the structure variables related to the protocol, I followed the NSCopying protocol in the Test classification

    This structure contains a_ protocol_t structure array
    _ protocol_t is defined as follows

    _ OBJC_ PROTOCOL_ The assignment of nscopying is as follows
  • Sixth member, (const struct _prop_list_t *)&_ OBJC_$_ PROP_ LIST_ Person_$_ Test
    Attribute related structure variables. In the Test category, a variable str of NSString type is written

When there is a method in a class and there are the same methods in multiple classifications, calling this method must execute the methods in the classification. The specific classification depends on the compilation order. Traverse the classification list in reverse order in the attachCategories method, and merge the method list, protocol list and attribute list contained in the classification into the corresponding large array, Insert the merged classification data in front of the original data of the class.
The traversal of the classification list is in reverse order, that is, the last classification involved in the compilation is first added to the beginning of the method list.
attachCategories(Class cls, category_list *cats, bool flush_caches) method is being implemented

In the log, eat is compiled first, and printing is the method of eat????

The one above should not be allowed. Ha ha ha

Control compilation order

Call priority
If there is a method with the same name as the original class in the classification, the method in the classification will be called first.
category - > this class - > parent class

category is loaded at run time, not at compile time

Call order of load method

The call timing of the load method is called by the system when loading classes and classifications at runtime
The load of each class and category is only called once during the program running

  • Call the load method of the class first
    • Call in the order of compilation (compile first, call first)
    • The load method of the parent class will be called before calling the load method of the child class
  • Then call the classified load method
    • Call in the order of compilation (compile first, call first)

The call of the load method (when the system automatically calls when loading memory) does not conform to the above call order
This is because the invocation of the load method is not through msgSend, it is directly addressing the address of the load method of the class, then calling the load method, then finding the classified load method, and then calling it.

In the above example, the test method is called through msgSend to find the class object first. If it is an instance method, find the test method in the class object (if it is a class method, find the metaclass object through the isa of the class object, and then find the test method). Because the classification also implements the test method and is placed in front of the test method of the class, the classified test method is called

For manually calling load, find the metaclass object of the class through msgSend. Since the classification also implements load, call the classified load.

initialize

Class calls initialize the first time it receives a message

Call through msgSend

  • When the parent class does not have initialize, first call the initialize of the parent class, and then call the initialize of the current class
    If the current class does not implement initialize, initialize of the parent class is called
    If multiple subclasses are not implemented, initialize of the parent class will be called multiple times
  • When the class implements initialize, it will override the initialize of this class

The difference between load and initialize

  • Call timing
    Load, which will be called when the runtime loads classes and classifications. The load method is always called before the main function. Each class and the load of classification are called only once at runtime.
    Initialize, which is called when the class receives the message for the first time (initialize the parent class first, and then the child class. Each class will be initialized only once)
  • Call order
    Load method: call the load method of the class first. The load method of the parent class will be called before the load method of the child class is called. The load method compiled first will be called first. Then call the classified load method, and call the first compiled method first
    Initialize method: call the initialize of the parent class first, and then the initialize of the current class. If the child class does not implement initialize, it will call the initialize of the parent class. If there is a classification, initialize of the last compiled classification is called
  • Call essence
    Load, call (* load_method) directly according to the IMP address (CLS, sel_load)
    initialize through objc_msgSend is called
  • Usage scenario
    Implement method switch in load method
    Generally used to initialize global or static variables
  • Same point
    Both methods are called automatically and cannot be called manually
  • difference
    load is called through the direct function address, and will only be called once
    initialize is called through msgSend
    • If the subclass does not implement initialize, the initializzie of the parent class will be called (so the initialize of the parent class will be called many times)
    • If the class implements initialize, it will override the initialize of the class itself,

What does the classification do at run time

  • Load all category data of a class through runtime
  • Traverse all categories, merge the methods, attributes and protocol data of the category into the corresponding large array, and traverse in reverse order, that is, the category data finally involved in the compilation will be in the first place of the array
  • Insert the merged classification data (methods, attributes and protocols) in front of the original data of the class

Why can't category add member variables

In the underlying structure of the classification, there is no member variable related list, so member variables cannot be declared

However, you can add attributes by associating objects.

Can a classification add attributes

You can add attributes, but adding attributes in the classification only has the declaration of the corresponding setter and getter, and does not generate the implementation of member variables, setters and getter methods

Use associated objects

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

- (NSString *)name {
    return objc_getAssociatedObject(self, @"name");
}

In the runtime source code

void _object_set_associative_reference(id object, void *key, id value, uintptr_t policy) {
    // retain the new value (if any) outside the lock.
    
    //Initialize an empty ObjcAssociation (Association object)
    ObjcAssociation old_association(0, nil);
    id new_value = value ? acquireValue(value, policy) : nil;
    {
        //Global manager
        AssociationsManager manager;
        //Get hashMap in manager
        AssociationsHashMap &associations(manager.associations());
        //Get the isguide value of the object as the key of AssociationHashMap
        disguised_ptr_t disguised_object = DISGUISE(object);
        
        //Value has value
        if (new_value) {
            // break any existing association.
            //AssociationsHashMap::iterator of type iterator
            //Find out whether the key in AssociationsHashMap is distinguished_ object
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i != associations.end()) {
                // secondary table exists
                //Get the ObjectAssociationMap corresponding to the key
                ObjectAssociationMap *refs = i->second;
                //Find out whether the ObjectAssociationMap has a key as the parameter key
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    //Get the ObjcAssociation (Association object) corresponding to the parameter key
                    old_association = j->second;
                    //Assign new values to associated objects
                    j->second = ObjcAssociation(policy, new_value);
                } else {
                    (*refs)[key] = ObjcAssociation(policy, new_value);
                }
            } else {
                // create the new association (first time).
                //This is the first time to add an associated object
                //Initialize ObjectAssociationMap
                ObjectAssociationMap *refs = new ObjectAssociationMap;
                associations[disguised_object] = refs;
                //assignment
                (*refs)[key] = ObjcAssociation(policy, new_value);
                object->setHasAssociatedObjects();
            }
        } else {  //Value if no value is nil, release the associated object corresponding to a key
            // setting the association to nil breaks the association.
            //
            AssociationsHashMap::iterator i = associations.find(disguised_object);
            if (i !=  associations.end()) {
                ObjectAssociationMap *refs = i->second;
                ObjectAssociationMap::iterator j = refs->find(key);
                if (j != refs->end()) {
                    old_association = j->second;
                    //Call erase to delete the corresponding associated object
                    refs->erase(j);
                }
            }
        }
    }
    // release the old value (outside of the lock).
    //Release old associated objects
    if (old_association.hasValue()) ReleaseValue()(old_association);
}
id _object_get_associative_reference(id object, void *key) {
    id value = nil;
    uintptr_t policy = OBJC_ASSOCIATION_ASSIGN;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            ObjectAssociationMap *refs = i->second;
            ObjectAssociationMap::iterator j = refs->find(key);
            if (j != refs->end()) {
                ObjcAssociation &entry = j->second;
                value = entry.value();
                policy = entry.policy();
                if (policy & OBJC_ASSOCIATION_GETTER_RETAIN) {
                    objc_retain(value);
                }
            }
        }
    }
    if (value && (policy & OBJC_ASSOCIATION_GETTER_AUTORELEASE)) {
        objc_autorelease(value);
    }
    return value;
}
//Delete all associated objects corresponding to the object object
void _object_remove_assocations(id object) {
    vector< ObjcAssociation,ObjcAllocator<ObjcAssociation> > elements;
    {
        AssociationsManager manager;
        AssociationsHashMap &associations(manager.associations());
        if (associations.size() == 0) return;
        disguised_ptr_t disguised_object = DISGUISE(object);
        AssociationsHashMap::iterator i = associations.find(disguised_object);
        if (i != associations.end()) {
            // copy all of the associations that need to be removed.
            ObjectAssociationMap *refs = i->second;
            for (ObjectAssociationMap::iterator j = refs->begin(), end = refs->end(); j != end; ++j) {
                elements.push_back(j->second);
            }
            // remove the secondary table.
            delete refs;
            associations.erase(i);
        }
    }
    // the calls to releaseValue() happen outside of the lock.
    for_each(elements.begin(), elements.end(), ReleaseValue());
}

  • The associated object is not stored in the memory of the associated object itself
  • Association objects are stored in a globally unified association manager
  • Setting the associated object to nil is equivalent to removing the associated object

Class extension

It is incorporated into the class when compiling

Extension
Extension is called extension, extension and anonymous classification by developers. Extension looks like an anonymous category, but they are almost two things.
Unlike category, extension can declare not only methods, but also attributes, members and variables.
extension is generally used to declare private methods, private properties, and private member variables

extension existence form
extension exists only in an. h file, or in an. m file of a class

The difference between category and extension

  • extension can add instance variables, but Category cannot add instance variables
  • extension is determined at compile time and is part of the class.
    category is determined during the run-time.
    extension forms a complete class with @ interface in the header file and @ implementation in the implementation file at compile time. extension will be generated with the generation of classes and will die together
  • Extension is generally used to hide the private information of a class. You must have the source code of a class to add extension to a class, so we can't add extension to a system class unless you create a subclass to add extension.
    Category does not need the source code of the class. We can add category classification to the classes provided by the system
  • Both extension and category can add attributes, but the attributes of category cannot generate the implementation of member variables and getter and setter methods

Tags: iOS

Posted on Tue, 14 Sep 2021 01:00:13 -0400 by dr bung