A Category of classification

1. The Category and source

Category is a pointer to the structure of a Category, defined as follows:

typedef struct objc_category *Category; struct objc_category { char *category_name OBJC2_UNAVAILABLE; // Class name char *class_name OBJC2_UNAVAILABLE; Struct objc_method_list *instance_methods OBJC2_UNAVAILABLE; Struct objc_method_list *class_methods OBJC2_UNAVAILABLE; // List of class methods struct objc_protocol_list *protocols OBJC2_UNAVAILABLE; // The list of protocols implemented by the classification}Copy the code

As can be seen from the structure, the classification energy

  • Add an instanceMethod to a class (instanceMethod)
  • Add a classMethod to a class
  • Implementation Protocol

There is no property list in this structure, so you cannot add instance variables, that is, you cannot automatically generate setters and getters for instance variables. Of course, we can add the class to the instance variables by associating objects.

The substantial reason a classification cannot add attributes

We know that if we declare a property in a class with @property, the compiler will automatically generate the _ member variable and the setter/getter for us, but there is no property list in the pointer structure of the class. So declaring a property in a class with @property doesn’t generate either the _ member variable or the setter/getter.

So the bottom line is: we can declare properties with @property, compile and run through, and not crash as long as we don’t use the program. But if you call the _ member variable and the setter/getter method, you will inevitably report an error.

The underlying structure of a Category is struct category_t, which stores object methods, class methods, attributes, and protocol information for the Category

struct category_t { const char *name; // The name of the class (name) classref_t CLS; // class (CLS) structmethod_list_t *instanceMethods; Struct method_list_t *classMethods; Struct protocol_list_t *protocols; // list of all classMethods added to the category (classMethods) struct protocol_list_t *protocols; // A list of all protocols implemented by the category. Struct property_list_t *instanceProperties; // All properties added to the category (instanceProperties)};Copy the code

As you can see from the category_t structure, categories can add instance methods, class methods, follow protocols, and add attributes; However, instance variables cannot be added.

You can have a property in a category, but that property only generates the declaration of getter and setter methods, and does not generate the corresponding implementation, let alone add the corresponding instance variable. If you want to add instance variables to an instance object, you can try using the associative reference technique.

2. Objective-c Associated Objects add attributes to categories

You cannot add attributes to a category directly, but you can add attributes to a category by using an associated object.

1) API is introduced

The associated object Runtime provides the following interfaces:

Void objc_setAssociatedObject(id object, const void *key, id value, Objc_getAssociatedObject (ID object, const void *key) // Remove all associated objects of an object. void objc_removeAssociatedObjects(id object)Copy the code

The key values must be unique. There are three recommended key values

  • Static char kAssociatedObjectKey; Use &kAssociateDobjectKey as key;
  • Static void *kAssociatedObjectKey = &kAssociatedObjectKey; Use kAssociatedObjectKey as the key value;
  • Use the selector, use the name of the getter method as the key value.

Enumeration value and description of objc_AssociationPolicy

Typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {OBJC_ASSOCIATION_ASSIGN = 0, // Specify an object associated with a weak reference. @property(assign)/@property(unsafe_unretained) OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, @property(unsafe_unretained) OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, @property(assign)/@property(unsafe_unretained) OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, @property(unsafe_unretained) @property(nonatomic,strong) OBJC_ASSOCIATION_COPY_NONATOMIC = 3, // specifies that the associated object is copied, nonatomic. @property(nonatomic,copy) OBJC_ASSOCIATION_RETAIN = 01401, // specify a strong reference to the associated object, atomicity. @property(atomic,strong) OBJC_ASSOCIATION_COPY = 01403 @property(atomic,copy) };Copy the code

In most cases, we use OBJC_ASSOCIATION_RETAIN_NONATOMIC’s association policy, which ensures that we hold the associated objects.

Objc_removeAssociatedObjects the objc_removeAssociatedObjects function is usually not used because it removes all associated objects from an object, restoring the object to its “original” state. Doing so is likely to remove the associated objects that others have added, which is not what we want. So the general way to do this is to remove an existing associated object by passing nil to objc_setAssociatedObject.

② Example

Add attributes for the system class :OC type name and simple type age

@property (strong, nonatomic) NSString *name; @property (assign, nonatomic) int age; - (void)setName:(NSString *)name {/** * associate an object with a class ** @param object#> object to associate description#> * @param key#> attribute key to associate Description# > * @param value#> the property you want to associate description#> * @param policy#> the modifier of the added member variable description#> */ objc_setAssociatedObject(self, @selector(name), name, OBJC_ASSOCIATION_COPY_NONATOMIC); } - (NSString *)name {/** * get the associated object of a class ** @param object#> associated object description#> * @param key#> key value of the property description#> */ return objc_getAssociatedObject(self, @selector(name)); } NSString * const recognizerAge = @"kAge"; - (void)setAge:(int)age{ objc_setAssociatedObject(self, (__bridge const void *)(kAge), @(age), OBJC_ASSOCIATION_ASSIGN); } - (int)age{ return [objc_getAssociatedObject(self, (__bridge const void *)(kAge)) intValue]; }Copy the code

3. The Category format

@interface Class to extend (the name of the class) @end @implementation name to extend (the name of the class) @endCopy the code

4. Do Category methods “override” methods of the same name in the original class?

  • The method of Category does not “completely replace” the method of the original class, that is, if both Category and the original class have methodA, then the class’s method list will have two methods after the Category appends

  • The Category method is placed at the top of the new method list, and the original class method is placed at the bottom of the new method list. This means that the Category method “overwrites” the original class method because the runtime is looking for methods in the order of the method list. As soon as it finds a method with the same name, it happily returns. It won’t meet the method with the same name after the meeting.

  • Methods of the same name are called in the order of compilation. For overridden methods, the corresponding method in the last compiled category is found first. Check the Build Phases -> Compile Sources for the project. The later the location, the later the compilation.

5. The Category

  • Reduce the size of individual files
  • The different functions are divided into different categories for easy management
  • You can load the categories you want on demand
  • Make Framework private methods public
  • Simulate multiple inheritance (protocol can also simulate multiple inheritance)

Such as:

Ibireme big god open source YYCategories, for the system class use classification expansion of small functions, very practical.

The blank page frame DZNEmptyDataSet perfectly handles the display of blank pages by using the classification function for UIScrollView

6. Implementation principle of Objective-C Associated Objects

The way to associate objects is

objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy) 
Copy the code

The core objects to implement the associated object technology are

AssociationsManager
AssociationsHashMap
ObjectAssociationMap
ObjcAssociation
Copy the code
  • ObjcAssociationStored in thevalueandpolicyThese two values.
  • ObjectAssociationMapIn order tokey-valueStored in the form ofkeyandObjcAssociation.
  • AssociationsHashMapIn order tokey-valueIn the form ofdisguised_objectandObjectAssociationMap. (The first argumentobjectafterDISGUISEThe function is converted to phidisguised_ptr_tThe type ofdisguised_object)
  • AssociationsManagerThere’s one insideAssociationsHashMapObject, and a spinlockspinlock_t

Summary of principles/How does the system manage associated objects

First, the system has a global AssociationsManager, which has an AssociationsHashMap hash table. The key in the hash table is the memory address of the object, and the value is the ObjectAssociationMap, which is also a hash table. Where key is the key that we set for the associated object, and value is ObjcAssociation, which holds the value set for the associated object and the memory management policy. Void objc_setAssociatedObject(ID object, const void * key, ID value, objc_AssociationPolicy Policy) First, the AssociationsHashMap is obtained from the AssociationsManager. Then, ObjectAssociationMap is retrieved from the AssociationsHashMap using the memory address of object as the key. If not, Create a new ObjectAssociationMap, and then get the old value from key, and generate the new value from key and policy ObjcAssociation(policy, new_value), and store the new value in ObjectAssociationMap. If the new value is not nil and the memory management policy is RETAIN, a retain is performed on the new value; if the new value is nil, the old value is deleted; if the old value is not empty and the memory management policy is RETAIN, a release is performed on the old value

2 the Extension Extension

Extension is a special case of Category. Class extensions have fewer names than classifications, so they are called “anonymous classifications.” Class extensions sound complicated, but we’ve known them for a long time. That’s what we use in our.m files

@interface ViewController () // Private property // Private method @endCopy the code

1. What is the Extension

  • Declaring a private property
  • Declaring a private method
  • Declare private member variables

2. What is the Extension

  • Compile time decision
  • Exists only as a declaration, most often parasitic in the.m of the host class
  • Generic private properties are written to class extensions in.m files
  • Cannot add an extension to a system class

3. The format of Extension

@interface XXX () // private property // private Method (Method definition for 'XXX' not found) @endCopy the code

The difference between Category and Extension

  • Classification is a run-time decision; Extensions are compile-time decisions; (If a method is not implemented, the compiler will alert the compiler, but if a method is not implemented, the compiler will not alert the compiler.)
  • In principle, classes can only add methods, and they are public (you can’t add properties directly, you can add properties through Runtime, because runtime solves the problem of no setter/getter); Extensions can add methods, instance variables, which default to type @private and apply only to their own class, not to subclasses or elsewhere;
  • The classification has its own implementation part; An extension has no implementation part of its own and can only be implemented by relying on the implementation part of the corresponding class.
  • Classification can add classification to a system class; Extensions cannot add extensions to system classes (you must have the source code for a class to add an Extension to a class);
  • Class extension methods defined in.m files are private. Class extension methods defined in.h files (header files) are public. Class extensions are a great way to declare private methods in.m files.