Deep Dive into Objective‑C Runtime: Understanding Categories and Their Loading Mechanism
The article explains Objective‑C categories by detailing their definition, compile‑time vs runtime differences, internal struct representation, how the compiler emits metadata, the loading and +load ordering process, method overriding mechanics, and the use of associated objects for adding state.
This article analyses the Objective‑C runtime implementation of category and explores all related aspects, including its definition, loading process, method overriding, and associated objects.
1. Category basics – Introduced in Objective‑C 2.0, a category allows adding methods to an existing class. Apple recommends using categories to split a class implementation into several files, declare private methods, or add optional functionality.
2. Category vs. Extension – An extension is a compile‑time feature that becomes part of the class definition and can add instance variables. A category is resolved at runtime, cannot add ivars, and only contributes methods, protocols, and properties.
3. Runtime representation – In the runtime a category is described by struct category_t (found in objc-runtime-new.h), which contains:
typedef struct category_t {
const char *name; // category name
classref_t cls; // target class
struct method_list_t *instanceMethods;
struct method_list_t *classMethods;
struct protocol_list_t *protocols;
struct property_list_t *instanceProperties;
} category_t;4. Compilation output – When compiling a file that defines a category, clang -rewrite-objc generates a large .cpp file. Near the end you can find the generated structures, e.g.:
static struct {
unsigned int entsize;
unsigned int method_count;
struct _objc_method method_list[1];
} _OBJC_$_CATEGORY_INSTANCE_METHODS_MyClass_$_MyAddition = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"printName", "v16@0:8", (void *)_I_MyClass_MyAddition_printName}}
};
static struct {
unsigned int entsize;
unsigned int count_of_properties;
struct _prop_t prop_list[1];
} _OBJC_$_PROP_LIST_MyClass_$_MyAddition = {
sizeof(_prop_t),
1,
{{"name","T@\"NSString\",C,N"}}
};5. Loading process – The runtime entry point _objc_init registers callbacks with dyld. During map_images the runtime discovers all category_t entries, creates a list catlist, and for each category it:
for (EACH_HEADER) {
category_t **catlist = _getObjc2CategoryList(hi, &count);
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
class_t *cls = remapClass(cat->cls);
if (!cls) { catlist[i] = NULL; continue; }
addUnattachedCategoryForClass(cat, cls, hi);
if (isRealized(cls)) { remethodizeClass(cls); }
}
}The function remethodizeClass merges the category’s method, property, and protocol lists into the target class, updates caches, and flushes vtables when needed.
6. +load order – Both classes and categories can implement +load. The runtime schedules the class +load first, then the categories in the order they were compiled. Log output confirms this sequence.
7. Method overriding – A category does not replace the original method; it inserts its methods at the front of the method list. Therefore the original implementation still exists, but the runtime finds the category’s method first, effectively overriding it. If multiple categories implement the same selector, the last compiled category wins.
8. Calling the original implementation – By enumerating the class’s method list you can locate the last IMP for a selector and invoke it manually:
Class cls = [MyClass class];
Method *list = class_copyMethodList(cls, &count);
for (NSUInteger i = 0; i < count; i++) {
Method m = list[i];
if (sel_isEqual(method_getName(m), @selector(printName))) {
IMP lastImp = method_getImplementation(m);
((void (*)(id, SEL))lastImp)(obj, @selector(printName));
}
}
free(list);9. Associated objects – Because categories cannot add ivars, they use the associated‑object API:
objc_setAssociatedObject(self, "name", name, OBJC_ASSOCIATION_COPY);
NSString *name = objc_getAssociatedObject(self, "name");The runtime stores all associations in a global AssociationsManager which holds a static AssociationsHashMap. Each entry maps an object pointer to another hash map of key → ObjcAssociation. When an object is deallocated, objc_destructInstance checks the flag _class_instancesHaveAssociatedObjects and calls _object_remove_assocations to clean up.
10. Conclusion – The Objective‑C runtime source is fully open (available from Apple’s open‑source site). This article provides a practical walkthrough of how categories are represented, compiled, loaded, and interact with the runtime, offering a solid foundation for deeper exploration.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Meituan Technology Team
Over 10,000 engineers powering China’s leading lifestyle services e‑commerce platform. Supporting hundreds of millions of consumers, millions of merchants across 2,000+ industries. This is the public channel for the tech teams behind Meituan, Dianping, Meituan Waimai, Meituan Select, and related services.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
