This article, the 10th in the Objective-C series, covers the implementation principles and features of categories.

  • Objective C (7) Object memory analysis
  • Objective C (8) The nature and classification of objects
  • Objective-c (9) KVC and KVO
  • Objective-c (10) Category
  • Objective-c load and Initialize
  • Objective-c (12) Associative objects

An overview,

1.1 introduction of the category

Category is a language feature added after Objective-C 2.0. Categories and categories actually refer to categories. The main purpose of a category is to add methods to an existing class.

You can separate the implementation of a class into several different files. There are several obvious benefits to doing so.

  • Organize different functions into different categories, reducing the size of individual files and making them easier to maintain;
  • A class can be created by multiple developers;
  • You can load the desired category on demand;
  • Declare private methods;

But in addition to apple’s recommended usage scenarios, there are several other usage scenarios derived from categories:

  1. Simulate multiple inheritance (protocol is another option)
  2. Expose the framework’s private methods

1.2 the extension

Extensions are what developers call extensions, extensions, and anonymous categories. Extension looks a lot like an anonymous category, but extension and category are almost entirely different things.

Unlike categories, extension can declare not only methods, but also attributes and member variables. Extension is generally used to declare private methods, private attributes, and private member variables.

To use Extension, you must have the source code for the original class. Extension declares methods, attributes, and member variables that must be implemented within the main @implementation interval of the class, avoiding multiple unnecessary implementation sections with named categories.

Extension is a common use for adding private variables and methods to a class for use inside the class. For example, defining a property of type Readonly in interface, adding extension to the implementation, redefining it as ReadWrite, so that we can change its value directly inside the class, but still cannot call setter methods outside to change it.

1.3 Difference between Category and Extension

In terms of the difference between category and extension,

  • Extension can add instance variables, while category cannot.
  • Extension is determined at compile time and is part of the class, while category is determined at run time. Extension is created at compile time along with the @Interface in the header file and @Implement in the implementation file to form a complete class, and extension is born and dies with the class.
  • Extension is generally used to hide private information about classes. You must have the source code of a class to add an extension to a class, so you cannot add an extension to a system class such as NSString unless you create a subclass and then add an extension. You don’t need to have class source code for a category. You can add a category to a class provided by the system.
  • Extension and category can both add attributes, but category attributes do not generate member variables and getter and setter implementations.

Second, category compilation time

First, we write the following classes, source code: 01-principle

Recompile to C++ code:

$ xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc BFPerson+Work.m
$ xcrun -sdk iphoneos clang -arch arm64 -rewrite-objc BFPerson+Study.m
Copy the code

Using Study classification as an example, let’s look at structures in C++ :

Some structures corresponding to initialization are as follows:

We see that at compile time, corresponding constructs are generated for instance methods, class methods, and attributes (which are also generated if there is a protocol; there is no protocol inheritance in the sample code) in the classification.

The resulting structure is _category_t in the figure above:

struct _category_t {
	const char *name;		/ / class name
	struct _class_t *cls; 	/ / class
	const struct _method_list_t *instance_methods;	// List of instance methods
	const struct _method_list_t *class_methods;		// List of class methods
	const struct _protocol_list_t *protocols;		// Protocol list
	const struct _prop_list_t *properties;			// Attribute list
};
Copy the code

Iii. The running period of category

Source code: 01-principle

3.1 What does classification do at run time

  • The Runtime loads all the category data for a class;

  • Combine all category methods (object methods, class methods), attributes, and protocol data into one large array

    • The category data that will be compiled later will come before the merged array;
  • Insert the merged classification data (methods, attributes, protocols) before the original data of the class;

Let’s trace these conclusions from the objC source code.

3.2 Category_T structure

//from objc-runtime-new.h
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

3.3 Source Code Introduction

Below the source code, we take the object method in the classification as an example. Class methods, protocols, or properties follow the same rules as object methods.

Figure 3.3.1 reading guidance

Source code execution process is as follows:

Here is a flowchart for merging the classification methods into the original class:

3.3.2 Core process

We read the core relayout object method:

A. Determine the classification compilation sequence

In project compilation, Compile Sources will determine the classification compilation order. In the figure below, Study classification comes first and Work classification comes second.

B. Obtain the category listcats

Get the list with “Study” in front of “Work”.

cats = [

​ category_t (BFPerson+Study),

​ category_t (BFPerson+Work)

]

C. willcatsMethod merge tomlists

The method of each classification is extracted from cats in reverse order and put into two-dimensional array Mlists. At this time, the Work object method is first:

mlists = [ ​ [method_t work, method_t test], ​ [method_t study, method_t test], ]

d. mlistsinsertrw

Insert the mlists method into the RW object method list method_array_t

  1. List the rW legacy methods[method_t test]Move to themethod_array_tThe last;
  2. The classificationmlistsMethod copy tomethod_array_tThe head.

E. Object method results

Finally, we get method_array_t in rW:

method_array_t = [

​ [method_t study, method_t test], <–>BFPerson+Work

​ [method_t work, method_t test], <–>BFPerson+Study

​ [ method_t test], <–>BFPerson

]

3.3.3 Core source code interception

Here is part of the extracted source code:

3.3.3.1 Classification methods

// cls - BFPerson
// *cats = [category_t (BFPerson+Study), category_t (BFPerson+Work) ]
static void 
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
    // Allocate memory: For a list of categories that have not been rearranged
    /*mlists method 2d array */
    method_list_t **mlists = (method_list_t **)
        malloc(cats->count * sizeof(*mlists));
    int i = cats->count;
    
    while (i--) {
        // I -- At the bottom of the category list, add to mlists first
        // Retrieve the categories in the category list
        Category_t BFPerson+Work
        // Compile order: view in Compile log, change in Compile Sources
        auto& entry = cats->list[i];
        // Retrieve the list of classified object methods
        method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
        if (mlist) {
            // Add the list of methods corresponding to the classification to mlists
            /* Cats = [category_t (BFPerson+Study), category_t (BFPerson+Work)] Mlists are [[method_t work, method_t test], [method_t study, method_t test],] */
            mlists[mcount++] = mlist;
            fromBundle |= entry.hi->isBundle();
        }
    }

    auto rw = cls->data();

    prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
    
    // Add the rearranged category list to the RW
    // Append all classified object methods to the list of class object methods
    rw->methods.attachLists(mlists, mcount);
    free(mlists);
    // Refresh the method cache list
    if (flush_caches  &&  mcount > 0) flushCaches(cls);
}
Copy the code

3.3.3.2 Method List Processing

Then start rearranging the list of methods in the RW:

/** @param addedLists classification method list ———— 2d array [[method_t work, method_t test], [method_t study, Method_t test]] @param addedCount Length of a class list ———— Number of classes */
void attachLists(List* const * addedLists, uint32_t addedCount) {
    if (addedCount == 0) return;

    if (hasArray()) {
        // many lists -> many lists
        uint32_t oldCount = array()->count;
        uint32_t newCount = oldCount + addedCount;
        // Reallocate instance object arraylist memory
        setArray((array_t *)realloc(array(), array_t::byteSize(newCount)));
        array()->count = newCount;

        // array()-> Lists original method list
        // *memmove(void *__dst, const void *__src, size_t __len);
        // Start with array()->lists, move oldCount length to array()-> Lists + addedCount
        // There are two of them (Study, Work)
        // 【1】【】【】-> 【】【】【1】
        memmove(array()->lists + addedCount, array()->lists, 
                oldCount * sizeof(array()->lists[0]));
        // *memcpy(void *__dst, const void *__src, size_t __n);
        // Copy the addedCount length from the beginning of addedLists to array()->lists
        // 【1】【】【 【】-> 【】【】【1】-> 【 2-work 】【 3-study 】【1】
        memcpy(array()->lists, addedLists, 
               addedCount * sizeof(array()->lists[0])); }}Copy the code

reference

link

  1. Apple souce objc4

The sample code

  1. 01-principle