No matter how perfect a class design is, there may be some unpredictable situations in the future requirements evolution. How to extend the existing classes? In general, inheritance and composition are good choices. However, in Objective-C 2.0, category is also provided, which can dynamically add new behaviors to existing classes. Today, category has been found in every corner of Objective-C code, from Apple’s official framework to various open-source frameworks, from large-scale apps with complex functions to simple applications, category is everywhere. This paper makes a comprehensive arrangement of category, hoping to benefit readers.
The function of category feature in Objective-C is as follows:
(1) the implementation of the class can be distributed to multiple different files or frameworks (supplement new methods).
(2) you can create a forward reference to a private method.
(3) you can add informal agreements to objects.
The limitations of category features in Objective-C are as follows:
(1) a class can only add new methods to the original class, and can only add but not delete or modify the original methods, and cannot add new attributes to the original class.
(2) the method added by a class to the original class is globally valid and has the highest priority. If it has the same name as the method of the original class, the original method will be overwritten unconditionally.
I. the underlying implementation of category
Objective-C implements the feature of dynamic language through runtime. All classes and objects are represented by structure in runtime. Category is represented by structure category in runtime. The following is the specific representation of structure category:
typedef struct category_t {
Const char * name; // the name of the main class
Classref_t CLS; // class
Structure method list * instancemethods; // list of instance methods
Struct method list * classmethods; // list of class methods
Struct protocol? List? T * protocols; // list of all protocols
Struct property list * instanceproperties; // all properties added
} category_t;
It can also be seen from the definition of category that category can be (instance method, class method, even protocol and attribute can be added) and cannot be (instance variable cannot be added).
We will explore the implementation principle of category based on the source code of runtime. Open the runtime source project, in the file (objc-runtime-new.mm
The following functions are found in:
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 processed as follows in this function:
(1) register the category and its main class (or metaclass) in the hash table;
(2) if the main class (or metaclass) has been implemented, rebuild its method list;
Implementation principle of category:
- At compile time, the methods implemented in the classification are generated into a structure method_list_t, generate a struct property list t from the declared properties, and then generate a struct category t from these structs.
- Then save the structure category
- At runtime, the runtime will get the structure category that we saved at compile time
- Then add the instance method list, protocol list and attribute list in the structure category to the main class
- Add the class method list and protocol list in the structure category? T to the main class’s metaclass
Two.Why do methods in category take precedence over methods in the original class?
//It's probably inserted like this
newproperties->next = cls->data()->properties;
cls->data()->properties = newproperties;,
3. Why can’t instance variables be added to category?
We can know from the structure category that we can add instance method, class method, protocol and attribute in category. There is no objc ﹣ Ivar ﹣ list structure here, which means that we can’t add instance variables to the classification.