- This post was originally posted on my personal blog:”Unruly Pavilion”
- Article links:portal
- This article was updated on July 24, 2019 at 20:15:36
This article introduces the underlying principles of categories in the Runtime in iOS development. Through this article, you will learn:
- Introduction to Category
- The essence of the Category
- The Category loading process
- The +load method for Category and Class
- Category and associated object
Example code in this article is: bujige/ysc-category -Demo
1. Introduction to Category
1.1 What is a Category?
Categories are objective-C 2.0 language features that add methods to existing classes. A Category can extend or separate a class by adding new methods to an existing class without subclassing or invading the source code of the class. In daily development, we often use categories to extend functionality to existing classes.
While inheritance can also add new methods to existing classes and directly add attributes, inheritance adds unnecessary code complexity and is indistinguishable from the parent class’s original methods at run time. So we can give priority to using custom categories.
Generally, categories are used in the following scenarios:
- Separate class implementations into separate files.
- Declare private methods.
- Simulate multiple inheritance.
- Expose framework private methods.
1.2 Category and Extension
A Category looks similar to an Extension. An Extension is sometimes called an anonymous classification. But they are fundamentally different things. Extension is compiled at the same time as the class at compile time and is part of the class. And the methods declared in Extension can only be implemented in the @implementation of that class, which means you can’t use Extension on system classes like NSString classes.
And unlike Category, Extension can declare not only methods but also member variables, which Category cannot.
Why can’t a Category add member variables like an Extension?
Because an Extension is compiled at the same time as the class, it is part of the class. Since they are part of a class and are compiled at the same time as the class, member variables can be added to the class at compile time.
Categories, on the other hand, have the ability to dynamically add new behavior to existing classes at run time. Categories are determined during run time. The memory layout of member variables is already determined at compile time, and adding member variables at run time would destroy the memory layout of the original class, with dire consequences, so categories cannot add member variables.
2. The essence of categories
2.1 Introduction to Category structure
In the first chapter of iOS development: “Runtime” in detail (1), we learned the basics: Objc-runtimenew.h objc-runtimenew.h objc-runtimenew.h objc-runtimenew.h Category is defined as category_T structure. The category_T structure has the following data structure:
typedef struct category_t *Category;
struct category_t {
const char *name; / / the name of the class
classref_t cls; // Class, which corresponds to the class object at runtime via clasee_name (class name)
struct method_list_t *instanceMethods; // List of all added object methods in the Category
struct method_list_t *classMethods; // List of all added class methods in the Category
struct protocol_list_t *protocols; // List of all protocols implemented in Category
struct property_list_t *instanceProperties; // All attributes added to the Category
};
Copy the code
You can also see from the structure definition of a Category that a Category can add object methods, class methods, protocols, and attributes to a class. At the same time, you can also see that categories cannot add member variables.
2.2 Category C++ source code
To understand the nature of categories, we need the C++ source code for them. First, we need to write a Person class that inherits from NSObject, and we need to write a category for Person+Additon. Add object methods, class methods, properties, and proxies to the classification.
For example, in the following code:
/ * * * * * * * * * * * * * * * * * * * * * Person + Addition. H file * * * * * * * * * * * * * * * * * * * * * /
#import "Person.h"
/ / PersonProtocol agent
@protocol PersonProtocol <NSObject>
- (void)PersonProtocolMethod;
+ (void)PersonProtocolClassMethod;
@end
@interface Person (Addition) <PersonProtocol>
/* name attribute */
@property (nonatomic.copy) NSString *personName;
/ / class methods
+ (void)printClassName;
// Object method
- (void)printName;
@end
/ * * * * * * * * * * * * * * * * * * * * * Person + Addition, m file * * * * * * * * * * * * * * * * * * * * * /
#import "Person+Addition.h"
@implementation Person (Addition)
+ (void)printClassName {
NSLog(@"printClassName");
}
- (void)printName {
NSLog(@"printName");
}
#pragma mark - <PersonProtocol>methods
- (void)PersonProtocolMethod {
NSLog(@"PersonProtocolMethod");
}
+ (void)PersonProtocolClassMethod {
NSLog(@"PersonProtocolClassMethod");
}
Copy the code
OC to C++ source code method as follows:
- Add the Person class files Person.h and Person.m to the project. The Person class inherits from NSObject.
- Add the Category files Person+ addition. h and Person+ addition. m for the Person class to the project, and add related object methods, class methods, properties, and proxies to the Category.
- Open Terminal and run
cd XXX/XXX
Command, whereXXX/XXX
Is the directory where the Category file resides.- Continue execution on the terminal
clang -rewrite-objc Person+Addition.m
- After executing the command, a Person+ addition. CPP file will be generated in the Person+ addition. m directory, which is the C++ source code for the Category we need.
When we get the Person+ addition.cpp file, it’s a magical thing: it’s 3.7 meters in size, with nearly 10W lines of code.
Don’t panic. The C++ source for the Category is at the bottom of the file. If we remove all the extraneous code and just keep the Category code, we’re left with something like 200 lines of code. Now let’s explain the Category structure by module according to its different structure.
2.2.1 “Category Structure”
// Category structure of the Person class
struct _category_t {
const char *name;
struct _class_t *cls;
const struct _method_list_t *instance_methods;
const struct _method_list_t *class_methods;
const struct _protocol_list_t *protocols;
const struct _prop_list_t *properties;
};
// Assign the Category structure of the Person class
static struct _category_t _OBJC_The $_CATEGORY_Person_The $_Addition __attribute__ ((used.section(" __DATA, __objc_const"))) =
{
"Person".0.// &OBJC_CLASS_$_Person,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Addition,
(const struct _method_list_t *)&_OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Addition,
(const struct _protocol_list_t *)&_OBJC_CATEGORY_PROTOCOLS_$_Person_$_Addition,
(const struct _prop_list_t *)&_OBJC_$_PROP_LIST_Person_$_Addition,
};
// Category array. If Person has multiple categories, there are multiple categories in the Category array
static struct _category_t *L_OBJC_LABEL_CATEGORY_$[1] __attribute__((used.section(" __DATA, __objc_catlist.regular.no_dead_strip"))) = {
&_OBJC_$_CATEGORY_Person_$_Addition,
};
Copy the code
From the “Category structure” source we can see:
- Categor structure.
- The assignment statement of the Category structure.
- Array of Category structures.
The first Categor structure corresponds in essence to the Categor structures described in the 2.1 Category introduction. You can view it as the same structure. The third Category structure array holds the related categories of the Person class, or the corresponding number of Category structures if there are more than one Category.
2.2.2 “Object Method List Structure” in Category
// - (void)printName; Object method implementation
static void _I_Person_Addition_printName(Person * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ct_0dyw1pvj6k16t5z8t0j0_ghw0000gn_T_Person_Addition_405207_mi_1);
}
// - (void)personProtocolMethod; Method implementation
static void _I_Person_Addition_personProtocolMethod(Person * self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ct_0dyw1pvj6k16t5z8t0j0_ghw0000gn_T_Person_Addition_f09f6a_mi_2);
}
// Object method list structure added to the Person category
static struct/ * _method_list_t* / {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2].
} _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Addition __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"printName"."v16@0:8", (void *)_I_Person_Addition_printName},
{(struct objc_selector *)"personProtocolMethod"."v16@0:8", (void *)_I_Person_Addition_personProtocolMethod}}
};
Copy the code
From the “object method list structure” source we can see:
- (void)printName;
Object method implementation.- (void)personProtocolMethod;
Method implementation.- Object method list structure.
Any object method implemented in a Category (including object methods in a proxy). Are added to the object method list structure _OBJC_$_CATEGORY_INSTANCE_METHODS_Person_$_Addition. If only defined in person.h and not implemented, it will not be added.
2.2.3 “Class Method List Structure” in Category
// + (void)printClassName; Class method implementation
static void _C_Person_Addition_printClassName(Class self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ct_0dyw1pvj6k16t5z8t0j0_ghw0000gn_T_Person_Addition_c2e684_mi_0);
}
// + (void)personProtocolClassMethod; Method implementation
static void _C_Person_Addition_personProtocolClassMethod(Class self, SEL _cmd) {
NSLog((NSString *)&__NSConstantStringImpl__var_folders_ct_0dyw1pvj6k16t5z8t0j0_ghw0000gn_T_Person_Addition_c2e684_mi_3);
}
// Class method list structure added to the Person category
static struct/ * _method_list_t* / {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[2].
} _OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Addition __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
2,
{{(struct objc_selector *)"printClassName"."v16@0:8", (void *)_C_Person_Addition_printClassName},
{(struct objc_selector *)"personProtocolClassMethod"."v16@0:8", (void *)_C_Person_Addition_personProtocolClassMethod}}
};
Copy the code
From the “class method list structure” source we can see:
+ (void)printClassName;
Class method implementation.+ (void)personProtocolClassMethod;
Class method implementation.- Class method list structure.
Any class method implemented in a Category (including the class method in a proxy). Will be added to the class method list structure _OBJC_$_CATEGORY_CLASS_METHODS_Person_$_Addition. If only defined in person.h and not implemented, it will not be added.
2.2.4 Protocol List Structure in Category
// Protocol list structure added to the Person category
static struct/ * _protocol_list_t* / {
long protocol_count; // Note, this is 32/64 bit
struct _protocol_t *super_protocols[1].
} _OBJC_CATEGORY_PROTOCOLS_$_Person_$_Addition __attribute__ ((used, section ("__DATA,__objc_const"))) = {
1,
&_OBJC_PROTOCOL_PersonProtocol
};
// Protocol list object Method list structure
static struct/ * _method_list_t* / {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1].
} _OBJC_PROTOCOL_INSTANCE_METHODS_PersonProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"personProtocolMethod"."v16@0:8".0}}};// Protocol list class method list structure
static struct/ * _method_list_t* / {
unsigned int entsize; // sizeof(struct _objc_method)
unsigned int method_count;
struct _objc_method method_list[1].
} _OBJC_PROTOCOL_CLASS_METHODS_PersonProtocol __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_objc_method),
1,
{{(struct objc_selector *)"personProtocolClassMethod"."v16@0:8".0}}};// Assign to the PersonProtocol structure
struct _protocol_t _OBJC_PROTOCOL_PersonProtocol __attribute__ ((used)) = {
0."PersonProtocol",
(const struct _protocol_list_t *)&_OBJC_PROTOCOL_REFS_PersonProtocol,
(const struct method_list_t *)&_OBJC_PROTOCOL_INSTANCE_METHODS_PersonProtocol,
(const struct method_list_t *)&_OBJC_PROTOCOL_CLASS_METHODS_PersonProtocol,
0.0.0.sizeof(_protocol_t),
0,
(const char **)&_OBJC_PROTOCOL_METHOD_TYPES_PersonProtocol
};
struct _protocol_t* _OBJC_LABEL_PROTOCOL_The $_PersonProtocol= & _OBJC_PROTOCOL_PersonProtocol;
Copy the code
From the “protocol list structure” source we can see:
- Protocol list structure.
- Protocol list object Method list structure.
- Protocol list class method list structure.
- PersonProtocol Protocol structure assignment statement.
2.2.5 “Attribute List Structure” in Category
// List of attributes added to the Person category
static struct/ * _prop_list_t* / {
unsigned int entsize; // sizeof(struct _prop_t)
unsigned int count_of_properties;
struct _prop_t prop_list[1].
} _OBJC_$_PROP_LIST_Person_$_Addition __attribute__ ((used, section ("__DATA,__objc_const"))) = {
sizeof(_prop_t),
1,
{{"personName"."T@\"NSString\",C,N"}}};Copy the code
From the “attribute list structure” source we see:
Only the attribute list structure _OBJC_$_PROP_LIST_Person_$_Addition is added to the Person category, and there is no member variable structure _ivar_list_t. There is nothing about the corresponding set/GET methods. This directly addresses the fact that you can’t add member variables to a Category.
2.3 Substantial summary of Category
Let’s summarize the nature of categories:
The essence of a Category is the _category_T structure type, which contains the following sections:
_method_list_t
Object method list structure of type;_method_list_t
Class method list structure of type;_protocol_list_t
Type of the protocol list structure;_prop_list_t
Type of the property list structure.The _category_T structure does not contain the _ivar_list_t type, that is, the “member variable structure”.
3. Category loading process
3.1 General process of DYLD loading
We talked earlier about the fact that categories are dynamically loaded at runtime. The Runtime loads without a dynamic linker called dyLD.
On MacOS and iOS, the dynamic link loader dyld is used to load all libraries and executables. The process of loading the Runtime occurs when the DYLD is loaded.
The code for DyLD is available for download on Apple’s open source website. Dyld Apple open source code
The dyLD loading process looks like this:
- Configure environment variables;
- Load the shared cache;
- Initialize the main APP;
- Insert dynamic cache library;
- Link main program;
- Link-inserted dynamic library;
- Initialization main program: OC, C++ global variable initialization;
- Returns the main program entry function.
In this article, we only need to care about step 7, because this is where the Runtime is initialized. Loading a Category is also part of this process.
In the main program, the Runtime initializes the call stack as follows:
dyldbootstrap::start
—>dyld::_main
—>initializeMainExecutable
—>runInitializers
—>recursiveInitialization
—>doInitialization
—>doModInitFunctions
—>_objc_init
The last call to _objc_init is a method in the LibobJC library that initializes the Runtime and is an Objective-C entry point.
The runtime code is available for download on Apple’s open source web site. Objc4 Apple open source code
In the _objc_init step: Runtime binds a callback to DYLD. When image is loaded into memory, DYLD notifies Runtime for processing. Runtime takes over and calls MAP_images for parsing and processing. Call the _read_images method to add the object methods, protocols, and attributes of the Category to the class, and the class methods and protocols to the metaclass of the class. The call_load_methods method is then called in load_images to iterate over all the loaded classes, calling the load method of the Class and the load method of its Category in order of inheritance and compilation.
The call stack for loading a Category is as follows:
_objc_init –> map_images –> map_images_NOLock –> _read_images (load classification) –> load_images.
Now that we know that Category loading happens in the _read_images method, we just need to focus on the code for Category loading in the _read_images method.
3.2 Category loading process
3.2.1 _read_images
methods
Ignore the other code in the _read_images method that is not relevant to this article and get the following:
// Get the classification array in the mirror
category_t **catlist =
_getObjc2CategoryList(hi, &count);
bool hasClassProperties = hi->info()->hasCategoryClassProperties();
// Iterate over the classification array
for (i = 0; i < count; i++) {
category_t *cat = catlist[i];
Class cls = remapClass(cat->cls);
// Handle this classification
// First, register the current classification with the target class
// Then, if the class is implemented, rebuild the class's list of methods
bool classExists = NO;
if (cat->instanceMethods || cat->protocols
|| cat->instanceProperties)
{
addUnattachedCategoryForClass(cat, cls, hi);
if(cls->isRealized()) { remethodizeClass(cls); classExists = YES; }}if (cat->classMethods || cat->protocols
|| (hasClassProperties && cat->_classProperties))
{
addUnattachedCategoryForClass(cat, cls->ISA(), hi);
if(cls->ISA()->isRealized()) { remethodizeClass(cls->ISA()); }}}Copy the code
Two main methods are used:
addUnattachedCategoryForClass(cat, cls, hi);
Add unattached categories for classesremethodizeClass(cls);
Rebuild the class’s method list
Two objectives are achieved through these two methods:
- the
Category (Category)
Object methods, protocols, and properties are added to the class. - the
Category (Category)
Class method, protocol added to the classmetaclass
On.
Here are the two methods mentioned above.
3.2.2 addUnattachedCategoryForClass(cat, cls, hi);
methods
static void addUnattachedCategoryForClass(category_t *cat, Class cls,
header_info *catHeader)
{
runtimeLock.assertLocked();
// Get the list of all unattached categories stored: cats
NXMapTable *cats = unattachedCategories();
category_list *list;
// Find the list of CLS unattached classes from cats list: list
list = (category_list *)NXMapGet(cats, cls);
if (!list) {
list = (category_list *)
calloc(sizeof(*list) + sizeof(list->list[0]), 1);
} else {
list = (category_list *)
realloc(list.sizeof(*list) + sizeof(list->list[0]) * (list->count + 1));
}
// Add the new category cat to the list
list->list[list->count++] = (locstamped_category_t){cat, catHeader};
// Inserting the newly generated list into cats overwrites the old list
NXMapInsert(cats, cls, list);
}
Copy the code
addUnattachedCategoryForClass(cat, cls, hi); You can refer to the code comments for the execution of the. After executing this method, the system places the current class cat into the list of unattached classes corresponding to the CLS of that class. Classes and categories are associated with each other.
RemethodizeClass (CLS); Methods.
3.2.3 remethodizeClass(cls);
methods
static void remethodizeClass(Class cls)
{
category_list *cats;
bool isMeta;
runtimeLock.assertLocked();
isMeta = cls->isMetaClass();
// Get the list of CLS unattached classes: cats
if ((cats = unattachedCategoriesForClass(cls, false/*not realizing*/))) {
// Append the list of unattached classes to the CLS class
attachCategories(cls, cats, true /*flush caches*/);
free(cats); }}Copy the code
remethodizeClass(cls); The attachCategories method basically does one thing: call attachCategories(CLS, cats, True); Method Append the list of unattached classes cats to CLS class. So again, attachCategories(CLS, Cats, True); Methods.
3.2.4 attachCategories(cls, cats, true);
methods
I swear this is the last piece of code to load a Category in this article. But it is also the most core piece of code.
static void
attachCategories(Class cls, category_list *cats, bool flush_caches)
{
if(! cats)return;
if (PrintReplacedMethods) printReplacements(cls, cats);
bool isMeta = cls->isMetaClass();
// Create a method list, attribute list, and protocol list to store the methods, attributes, and protocols of the classification
method_list_t **mlists = (method_list_t* *)malloc(cats->count * sizeof(*mlists));
property_list_t **proplists = (property_list_t* *)malloc(cats->count * sizeof(*proplists));
protocol_list_t **protolists = (protocol_list_t* *)malloc(cats->count * sizeof(*protolists));
// Count backwards through cats to get newest categories first
int mcount = 0; // Record the number of methods
int propcount = 0; // Record the number of attributes
int protocount = 0; // Record the number of protocols
int i = cats->count; // Start at the end of the category array, ensuring that the latest category is fetched first
bool fromBundle = NO; // Whether the record is fetched from the bundle
while (i--) { // iterate from back to front
auto& entry = cats->list[i]; // Retrieve the current category
// Retrieve the list of methods in the classification. If it's a metaclass, it gets a list of class methods. Otherwise you get a list of object methods
method_list_t *mlist = entry.cat->methodsForMeta(isMeta);
if (mlist) {
mlists[mcount++] = mlist; // Put the method list into the mlists method list array
fromBundle |= entry.hi->isBundle(); // Remember whether the bundle is stored in the header of the class
}
// Get the list of attributes in the class. If it's a metaclass, get nil
property_list_t *proplist =
entry.cat->propertiesForMeta(isMeta, entry.hi);
if (proplist) {
proplists[propcount++] = proplist;
}
// Retrieve the list of protocols followed in the classification
protocol_list_t *protolist = entry.cat->protocols;
if(protolist) { protolists[protocount++] = protolist; }}// Retrieve the class_rw_t data of the current CLS class
auto rw = cls->data();
// Store an array of methods, attributes, and protocols into the RW
// Prepare method list methods in mlists
prepareMethodLists(cls, mlists, mcount, NO, fromBundle);
// Add the list of new methods to the list of methods in rW
rw->methods.attachLists(mlists, mcount);
// Release method list mlists
free(mlists);
// Clear the CLS cache list
if (flush_caches && mcount > 0) flushCaches(cls);
// Add the new property list to the property list in the RW
rw->properties.attachLists(proplists, propcount);
// Release the property list
free(proplists);
// Add the new protocol list to the protocol list in rW
rw->protocols.attachLists(protolists, protocount);
// Release protocol list
free(protolists);
}
Copy the code
From attachCategories (CLS, cats, true); You can see from the annotation of the method that this method is the core code that stores the methods, attributes, and protocols of the classification.
But there are a few details to note:
- The methods, attributes, and protocols of a Category are only added to the original class, without completely replacing the methods, attributes, and protocols of the original class. An example of this is: suppose the original class owns
MethodA
Methods, categories also haveMethodA
Method, then after the class is loaded, the class will have two methods in the listMethodA
Methods.- The methods, attributes, and protocols of a Category are added to the top of the list of methods, attributes, and protocols of the original class, while the methods, attributes, and protocols of the original class are moved to the bottom of the list. Because methods are searched at runtime along the list of methods, Category methods are found first and executed directly, while the methods of the original class are not executed. This is the most immediate reason why the methods in a Category override the methods of the original class.
4. +load methods for Category and Class
The methods, attributes, and protocols in the Category are appended to the class before the + load method executes. That is, the methods, attributes, and protocols in the Category are already loaded in the class before the + load method is executed.
The order of the Category and Class + load methods is as follows:
- First call the main class, according to the compile order, according to the inheritance relationship from the parent class to the child class call;
- Call the main class, then call the classification, according to the compile order, call; ı II
+ load
Methods are called only once unless actively called.
Using this call rule, we know that the main class’s + load method call must precede the class’s + load method call. However, the classification + load method is not called in accordance with the inheritance relationship, but in accordance with the compile order, which also leads to the call order of the + load method is not always determined. One order may be: Parent -> subclass -> parent category -> subclass category, or parent -> Subclass -> subclass category -> parent category.
5. Category and associated objects
As we mentioned earlier, you can add attributes to a Category, but you don’t generate corresponding member variables, and you don’t generate getters and setters. As a result, an error is reported when you call the properties declared in the Category.
So there’s no way to use the Category properties?
The answer, of course, is no.
We can implement getters and setters ourselves and use Objective-C Associated Objects to implement getters and setters. Associative objects can help us associate arbitrary properties to an object at run time. Specific needs to use the following methods:
// 1. Pass key: Void objc_setAssociatedObject(id object, const void *key, id value, objc_AssociationPolicy policy); // 2. Obtain the associated attribute object ID objc_getAssociatedObject(id object, const void *key); // 3. Remove the attribute associated with the object void objc_removeAssociatedObjects(id object);Copy the code
Here is an example.
5.1 Added network address attributes to UIImage classification
/ * * * * * * * * * * * * * * * * * * * * * UIImage + Property. H file * * * * * * * * * * * * * * * * * * * * * /
#import <UIKit/UIKit.h>
@interface UIImage (Property)
/* Image network address */
@property (nonatomic.copy) NSString *urlString;
// Clear associated objects
- (void)clearAssociatedObjcet;
@end
/ * * * * * * * * * * * * * * * * * * * * * UIImage + Property. The m file * * * * * * * * * * * * * * * * * * * * * /
#import "UIImage+Property.h"
#import <objc/runtime.h>
@implementation UIImage (Property)
/ / set methods
- (void)setUrlString:(NSString *)urlString {
objc_setAssociatedObject(self.@selector(urlString), urlString, OBJC_ASSOCIATION_COPY_NONATOMIC);
}
/ / get methods
- (NSString *)urlString {
return objc_getAssociatedObject(self.@selector(urlString));
}
// Clear the associated object
- (void)clearAssociatedObjcet {
objc_removeAssociatedObjects(self);
}
@end
Copy the code
Test code:
UIImage *image = [[UIImage alloc] init];
image.urlString = @"http://www.image.png";
NSLog(@"image urlString = %@",image.urlString);
[image clearAssociatedObjcet];
NSLog(@"image urlString = %@",image.urlString);
Copy the code
Print result: 2019-07-24 18:36:31.051789+0800 ysc-category [74564:17944298] image urlString = www.image.png 2019-07-24 18:36:31.051926+0800 ysc-category [74564:17944298] image urlString = (null)
As can be seen: with the aid of the association object, we successfully added the urlString association attribute to the UImage class in UIImage classification, and implemented the getter and setter methods.
Note: You can disconnect all associations using objc_removeAssociatedObjects. It is generally not recommended because it breaks all connections. If you want to disassociate the association you can use objc_setAssociatedObject, just pass the associated object to nil.
The resources
- Meituan technical team: In-depth understanding of Objective-C: Category
- CJS_ : Summary of the underlying implementation principle of iOS classification
- Run-time apps for iOS
- Objc4 apple open source code | in this reference: objc4-750 version
- Dyld apple open source code | in this reference: dyld – version 635.2
The last
Finally, I just wanted to write about categories and associated objects at first. Accidentally touching the bottom of a Category… And then I accidentally wrote too much. Heart tired…
If the article is wrong, please correct, thank you.
IOS Development: Runtime
- IOS development: “Runtime” details (1) Basic knowledge
- IOS development: “Runtime” details (ii) Method Swizzling
- IOS development: “Runtime” details (3) Category underlying principles
- IOS development: “Runtime” detailed explanation (4) obtain the class detailed attributes, methods
Not yet completed:
- IOS development: “Runtime” details (5) Crash protection system
- IOS development: “Runtime” (6) Objective-C 2.0
- IOS development: “Runtime” details (seven) KVO low-level implementation