No matter how perfect a class design is, there may be some unpredictable situations in the future demand evolution. So how do you extend existing classes? Generally speaking, inheritance and combination are good choices. However, in Objective-C 2.0, category is also provided, which can dynamically add new behaviors to existing classes. Category is now available in every corner of Objective-C code, from Apple's official framework to open source frameworks, from large and complex APP s to simple applications, catagory is everywhere. In this paper, category has done a more comprehensive collation, hoping to benefit readers.
The role of category characteristics in Objective-C is as follows:
(1) Class implementations can be decentralized into multiple different files or multiple different frameworks (adding new methods).
(2) Forward references to private methods can be created.
(3) Informal protocols can be added to objects.
The limitations of category characteristics in Objective-C are as follows:
(1) Categories can only add new methods to the original class, and can only add, but not delete or modify the original method, and can not add new attributes to the original class.
(2) The method added to the original class by category is globally effective and has the highest priority. If the method is renamed with the original class, the original method will be overwritten unconditionally.
1. The underlying implementation of Category
Objective-C implements dynamic language through Runtime runtime. All classes and objects are represented by structure in Runtime. Category is represented by structure category_t in Runtime. The following is the concrete expression of structure category_t:
typedef struct category_t { const char *name;//Class Name Main Class Name classref_t cls;//class struct method_list_t *instanceMethods;//List of Instance Methods struct method_list_t *classMethods;//List of class methods struct protocol_list_t *protocols;//List of all protocols struct property_list_t *instanceProperties;//All attributes added } category_t;
From the definition of category, we can see that category can be (instance method, class method, even protocol implementation, attribute addition) and not (instance variable cannot be added).
We will explore the implementation principle of Category with the source code of runtime. Open the runtime source project and find the following functions in the file objc-runtime-new.mm:
void _read_images(header_info **hList, uint32_t hCount) { ... _free_internal(resolvedFutureClasses); } // Discover categories. for (EACH_HEADER) { category_t **catlist = _getObjc2CategoryList(hi, &count); for (i = 0; i < count; i++) { category_t *cat = catlist[i]; Class cls = remapClass(cat->cls); if (!cls) { // Category's target class is missing (probably weak-linked). // Disavow any knowledge of this category. catlist[i] = nil; if (PrintConnecting) { _objc_inform("CLASS: IGNORING category \?\?\?(%s) %p with " "missing weak-linked target class", cat->name, cat); } continue; } // Process this category. // First, register the category with its target class. // Then, rebuild the class's method lists (etc) if // the class is realized. BOOL classExists = NO; if (cat->instanceMethods || cat->protocols || cat->instanceProperties) { addUnattachedCategoryForClass(cat, cls, hi); if (cls->isRealized()) { remethodizeClass(cls); classExists = YES; } if (PrintConnecting) { _objc_inform("CLASS: found category -%s(%s) %s", cls->nameForLogging(), cat->name, classExists ? "on existing class" : ""); } } if (cat->classMethods || cat->protocols /* || cat->classProperties */) { addUnattachedCategoryForClass(cat, cls->ISA(), hi); if (cls->ISA()->isRealized()) { remethodizeClass(cls->ISA()); } if (PrintConnecting) { _objc_inform("CLASS: found category +%s(%s)", cls->nameForLogging(), cat->name); } } } } // Category discovery MUST BE LAST to avoid potential races // when other threads call the new category code before // this thread finishes its fixups. // +load handled by prepare_load_methods() ... }
We can see that Category is treated in this function as follows:
(1) Register Category and its main class (or metaclass) into the hash table;
(2) If the main class (or metaclass) has been implemented, rebuild its method list;
Principle of Category:
- During compilation, the method implemented in the classification is generated as a structure method_list_t, the declared attribute is generated as a structure property_list_t, and then a structure category_t is generated through these structures.
- Then save the structure category_t
- At runtime, Runtime gets the structure category_t we saved at compile time
- Then add the list of instance methods, protocols and attributes in the structure category_t to the main class.
- Add the list of class methods and protocols in the structure category_t to the metaClass of the main class
Second, why is the method priority in Category higher than the method in the original class?
// It's probably like inserting in this way. newproperties->next = cls->data()->properties; cls->data()->properties = newproperties;,