The Runtime feature is mainly message (method) passing, if the message (method) is not found in the object, forward, how to implement. Let’s explore the implementation mechanism of Runtime from the following aspects.
- The Runtime is introduced
- Runtime messaging
- Runtime Message forwarding
- The Runtime application
The Runtime is introduced
Objective-c extends the C language and adds object-oriented features and Smalltalk-style messaging. At the heart of this extension is a Runtime library written in C and a compiled language. It is the cornerstone of Objective-C’s object-oriented and dynamic mechanisms.
Objective-c is a dynamic language, which means that it requires not only a compiler, but also a runtime system to dynamically create classes and objects, and to deliver and forward messages. Understanding Objective-C’s Runtime mechanism can help you understand the language better and extend it as appropriate to solve design or technical problems in a project at the system level. To understand Runtime, you need to understand Messaging at its core.
There are actually two versions of Runtime: “Modern” and “Legacy.” We’re using Objective-C 2.0 with the Modern version of Runtime, which only works with 64-Bit applications after iOS and macOS 10.5. Older 32-bit macOS programs still use the (early) Legacy version of Runtime in Objective-C 1. The biggest difference between the two versions is that when you change the layout of the instance variables of a class, in the earlier version you had to recompile its subclasses, whereas in the current version you don’t.
Runtime is basically written in C and assembly, demonstrating apple’s efforts to make dynamic systems more efficient. You can access the open source code maintained by Apple here. Apple and GNU each maintain an open source version of Runtime, and the two versions struggle to be consistent.
In our daily business, we mainly use the official Api to solve our framework needs.
High-level programming language to be executable files need to be compiled to first assembly language to assembly language for the machines, machine language is also a computer can recognize the only language, but OC is not directly compiled to assembly language, but need to transfer is pure C language to compile and assembly operations, from OC to the transition of the C language is implemented by the runtime. However, we use OC for object-oriented development, while C language is more process-oriented development, which requires the transformation of object-oriented classes into process-oriented structures.
Runtime messaging
An object method looks like [obj foo], the compiler sends objc_msgSend(obj, foo) as a message, and Runtime executes the following process:
- First of all, through
obj
theisa
Pointer to find itclass
; - in
class
的method list
找foo
; - if
class
Not tofoo
Go on to itssuperclass
Find; - Once you’ve found
foo
This function, then, executes its implementationIMP
。
The problem with this implementation is that it is inefficient. But only 20% of the functions of a class are often called, perhaps 80% of the total calls. It doesn’t make sense for objc_method_list to be traversed once per message. You can greatly improve the efficiency of function queries if you cache functions that are often called. That’s what objc_cache, another important member of objc_class, does – after finding Foo, it saves Foo’s method_name as key and method_IMP as value. When you receive a message from Foo again, you can find it directly in the cache, avoiding traversing objc_method_list. As you can see from the previous source code, objc_cache exists in an objc_class structure.
The methods of objec_msgSend are defined as follows:
OBJC_EXPORT id objc_msgSend(id self, SEL op, ...)
Copy the code
So how is messaging implemented? Let’s look at the structure of object, class, and method:
Struct objc_object {Class isa OBJC_ISA_AVAILABILITY; }; // Struct objc_class {Class isa OBJC_ISA_AVAILABILITY;#if ! __OBJC2__
Class super_class OBJC2_UNAVAILABLE;
const char *name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list *ivars OBJC2_UNAVAILABLE;
struct objc_method_list **methodLists OBJC2_UNAVAILABLE;
struct objc_cache *cache OBJC2_UNAVAILABLE;
struct objc_protocol_list *protocols OBJC2_UNAVAILABLE;
#endif} OBJC2_UNAVAILABLE; Struct objc_method_list {struct objc_method_list *obsolete OBJC2_UNAVAILABLE; int method_count OBJC2_UNAVAILABLE;#ifdef __LP64__
int space OBJC2_UNAVAILABLE;
#endif/* variable length structure */ struct objc_method method_list[1] OBJC2_UNAVAILABLE; } OBJC2_UNAVAILABLE; // Method struct objc_method {SEL method_name OBJC2_UNAVAILABLE; char *method_types OBJC2_UNAVAILABLE; IMP method_imp OBJC2_UNAVAILABLE; }Copy the code
- The system first finds the receiving object of the message and then passes through the object’s
isa
Find its class. - Look in its class
method_list
If there isselector
Methods. - If not, look for the parent class
method_list
. - Find the corresponding
method
, to execute itIMP
. - forwarding
IMP
thereturn
Value.
Here are some of the concepts used in messaging:
- Class object (objc_class)
- Instance (objc_object)
- The metaclass (Meta Class)
- Method(objc_method)
- SEL(objc_selector)
- IMP
- Class cache (objc_cache)
- Category(objc_category)
Class object (objc_class)
Objective-c classes are represented by the Class type, which is actually a pointer to an objC_class structure.
typedef struct objc_class *Class;
Copy the code
Look at objc/ Runtime. h for the definition of the objc_class structure:
struct objc_class {
Class _Nonnull isa OBJC_ISA_AVAILABILITY;
#if ! __OBJC2__
Class _Nullable super_class OBJC2_UNAVAILABLE;
const char * _Nonnull name OBJC2_UNAVAILABLE;
long version OBJC2_UNAVAILABLE;
long info OBJC2_UNAVAILABLE;
long instance_size OBJC2_UNAVAILABLE;
struct objc_ivar_list * _Nullable ivars OBJC2_UNAVAILABLE;
struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE;
struct objc_cache * _Nonnull cache OBJC2_UNAVAILABLE;
struct objc_protocol_list * _Nullable protocols OBJC2_UNAVAILABLE;
#endif
} OBJC2_UNAVAILABLE;
Copy the code
Struct objc_class struct objc_class struct contains many variables. The struct contains a pointer to the parent class, the name of the class, the version of the class, the size of the instance, the list of instance variables, the list of methods, the cache, the list of protocols to follow, and so on. Yes, a Class object isa struct objc_class that holds data called metadata, and its first member variable is also a pointer to isa, which means that the Class itself is an object, so we call it a Class object. Class objects are generated at compile time to create instance objects and are singletons.
Instance (objc_object)
/// Represents an instance of a class.
struct objc_object {
Class isa OBJC_ISA_AVAILABILITY;
};
/// A pointer to an instance of a class.
typedef struct objc_object *id;
Copy the code
The metadata in class objects stores information about how to create an instance, so where should class objects and methods be created from? The isa pointer to aclass object is referred to as a metaclass. The metaclass contains all the information needed to create the class object and its methods. Therefore, the structure should look like the figure below:
The metaclass (Meta Class)
The struct objc_object instance has a pointer to a class object, the isa pointer to a class object points to a metaclass, and the super_class pointer points to a superclass object. The metaclass’s super_class pointer points to the metaclass’s superclass, and the metaclass’s ISA pointer points to itself.
A Meta Class is a Class of a Class object. As mentioned above, all classes are themselves an object to which we can send messages (that is, call class methods). In order to call class methods, the CLASS’s ISA pointer must point to an Objc_class structure that contains those class methods. This leads to the concept of meta-classes, which hold all the information needed to create class objects and class methods. Any meta-class of NSObject inheritance uses the Meta-class of NSObject as its own owning class, and the ISA pointer to the meta-class of the base class is to itself.
Method(objc_method)
Let’s look at the definition
runtime.h
/// An opaque type that represents a method inA class definition. Typedef struct objc_method *Method; struct objc_method { SEL method_name OBJC2_UNAVAILABLE; char *method_types OBJC2_UNAVAILABLE; IMP method_imp OBJC2_UNAVAILABLE;Copy the code
A Method is a piece of code that can perform a function independently. For example:
- (void)logName
{
NSLog(@"name");
}
Copy the code
This code right here, it’s just a function.
Let’s look at the contents of the objc_method structure:
- SEL method_name Method name
- Char *method_types Method type
- IMP method_IMP implementation
In this structure weight, we have seen SEL and IMP, indicating that SEL and IMP are in fact properties of Method.
Let’s move on to SEL.
SEL(objc_selector)
Let’s look at the definition
Objc.h
/// An opaque typethat represents a method selector. Typedef struct objc_selector *SEL;Copy the code
The second argument to objc_msgSend is of type SEL, which is the type of selector represented in Objective-C (in Swift, the selector class). A selector is a method selector, which can be thought of as an ID that distinguishes a method, and the data structure for that ID is SEL:
@property SEL selector;
Copy the code
You can see that selector is an instance of SEL.
A method selector is A C string that has been registered (or "mapped") with the objective-C runtime. Selectors generated by the compiler are automatically mapped by the runtime when the class is loaded.Copy the code
Selector is just a C string that maps to a method. You can get a method selector of type SEL by using the Objective-C compiler command @selector() or the Runtime system’s sel_registerName function.
Since selector is a string, I think it should be a combination of className and method. There are two naming rules:
- You can’t duplicate selectors for the same class
- Selector can be repeated for different classes
One of the disadvantages of this is that when we’re writing C code, we often use function overloading, which is the same function name, different arguments, but that doesn’t work in Objective-C, because the selector only has the name of the method, no arguments, so there’s no way to distinguish between different methods.
Such as:
- (void)caculate(NSInteger)num;
- (void)caculate(CGFloat)num;
Copy the code
There will be an error.
We can only tell by name:
- (void)caculateWithInt(NSInteger)num;
- (void)caculateWithFloat(CGFloat)num;
Copy the code
Methods with the same name in different classes will have the same method selector, even if they have the same name and different variable types.
IMP
Take a look at the definition of IMP
/// A pointer to the functionTypedef id (*IMP)(id, SEL,...) ;#endif
Copy the code
It’s a pointer to the memory address of the final implementation.
In the Runtime of iOS, Method implements a quick query Method and implementation through the selector and IMP properties, improving performance while maintaining flexibility.
Class cache (objc_cache)
When the Objective-C runtime checks objects by tracking its ISA pointer, it can find an object that implements many methods. However, you may only call a small portion of them, and it doesn’t make sense to search the class dispatch table for all the selectors every time you look. So the class implements a cache, and every time you search a class dispatch table and find the appropriate selector, it puts it in its cache. So when objc_msgSend looks for a class selector, it first searches the class cache. This is based on the theory that if you call a message on a class, you may call that message again later.
In order to speed up message distribution, the system caches the methods and their addresses in objc_cache, so in practice, most of the common methods are cached, and the Runtime system is actually very fast, close to the speed of programs that execute memory addresses directly.
Category(objc_category)
Category is a pointer to the structure of a Category, defined as follows:
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;
};
Copy the code
Name: class_name, not category_name. CLS: The class object to be extended is not defined at compile time, but is referred to the corresponding class object by name during Runtime. InstanceMethods: a list of all instanceMethods added to a class in a category. ClassMethods: a list of all classMethods added to a category. Protocols: A list of all protocols implemented by the category. InstanceProperties: That's all the properties in the Category, and that's why we can add instance variables with objc_setAssociatedObject and objc_getAssociatedObject, but that's not the same thing as a normal instance variable.Copy the code
From the abovecategory_t
We can see that the class can add instance methods, class methods, and even implement protocols, add attributes, but can not add member variables.
Runtime Message forwarding
As described earlier, a send message searches for a list of methods in the associated class object, and if not, searches up the inheritance tree to the root (usually NSObject), If still can’t find and forward failed returned to perform doesNotRecognizeSelector: method to unrecognized selector that wrong. So what exactly is message forwarding? The next three opportunities will be presented one by one.
- Dynamic method analysis
- Alternate receiver
- Complete message forwarding
Dynamic method analysis
First, the Objective-C runtime calls +resolveInstanceMethod: or +resolveClassMethod:, giving you the opportunity to provide a function implementation. If you add the function and return YES, the runtime system will restart the message sending process once.
An example of implementing a dynamic method parsing is as follows:
- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view, Typically from a nib. // Run the foo function [self performSelector:@selector(foo:)]; } + (BOOL)resolveInstanceMethod:(SEL)sel {if(selectors == @selector(foo:)) {selectors == @selector(foo:)) {selectors == @selector(foo:)) {selectors == @selector(foo:)) {selectors == @selector(foo:)) {selectors == @selector(foo:)) {selectors == @selector(foo:)) {selectors == @selector(foo:);"v@:");
return YES;
}
return [super resolveInstanceMethod:sel];
}
void fooMethod(id obj, SEL _cmd) {
NSLog(@"Doing foo"); // New foo function}Copy the code
2018-04-01 12:23:35.952670+0800 ocram[87546:23235469] Doing foo
You can see that while foo: is not implemented, we dynamically add the fooMethod function through class_addMethod and execute the IMP for fooMethod. According to the print, it worked.
If the resolve method to return NO, runtime will move to the next step: forwardingTargetForSelector.
Alternate receiver
Implements – forwardingTargetForSelector: if the target object, the Runtime will call this method at this moment, give you the opportunity to put forward this message to other objects.
An example of implementing an alternate receiver is as follows:
#import "ViewController.h"
#import "objc/runtime.h"
@interface Person: NSObject
@end
@implementation Person
- (void)foo {
NSLog(@"Doing foo"); //Person foo function} @end @interface ViewController () @end @implementation ViewController - (void)viewDidLoad {[super viewDidLoad]; // Do any additional setup after loading the view, Typically from a nib. // Run the foo function [self performSelector:@selector(foo)]; } + (BOOL)resolveInstanceMethod:(SEL)sel {returnYES; / / return YES, enter the next step forward} - (id) forwardingTargetForSelector aSelector: (SEL) {if (aSelector == @selector(foo)) {
return[Person new]; // Return the Person object to receive the message}return [super forwardingTargetForSelector:aSelector];
}
@end
Copy the code
2018-04-01 12:45:04.757929+0800 ocram[88023:23260346] Doing foo
You can see us through forwardingTargetForSelector ViewController in the current method is forwarded to the Person to perform. The printout also proves that we have successfully achieved the forwarding.
Complete message forwarding
If unknown messages cannot be processed in the previous step, the only thing you can do is to enable the full message forwarding mechanism. First of all, it will send – methodSignatureForSelector: message function parameters and return values of the type. If – methodSignatureForSelector: returns nil, the Runtime will send – doesNotRecognizeSelector: news, program will hang up now. If a function signature is returned, Runtime creates an NSInvocation object and sends the -forwardInvocation: message to the target object.
An example of implementing a full forward is as follows:
#import "ViewController.h"
#import "objc/runtime.h"
@interface Person: NSObject
@end
@implementation Person
- (void)foo {
NSLog(@"Doing foo"); //Person foo function} @end @interface ViewController () @end @implementation ViewController - (void)viewDidLoad {[super viewDidLoad]; // Do any additional setup after loading the view, Typically from a nib. // Run the foo function [self performSelector:@selector(foo)]; } + (BOOL)resolveInstanceMethod:(SEL)sel {returnYES; / / return YES, enter the next step forward} - (id) forwardingTargetForSelector aSelector: (SEL) {returnnil; / / returns nil, enter the next step forward} - (NSMethodSignature *) methodSignatureForSelector aSelector: (SEL) {if ([NSStringFromSelector(aSelector) isEqualToString:@"foo"]) {
return [NSMethodSignature signatureWithObjCTypes:"v@:"]; // To sign the invocation, go to forwardInvocation}return [super methodSignatureForSelector:aSelector];
}
- (void)forwardInvocation:(NSInvocation *)anInvocation {
SEL sel = anInvocation.selector;
Person *p = [Person new];
if([p respondsToSelector:sel]) {
[anInvocation invokeWithTarget:p];
}
else {
[self doesNotRecognizeSelector:sel];
}
}
@end
Copy the code
2018-04-01 13:00:45.423385+0800 ocram[88353:23279961] Doing foo
Judging from the print, we achieved a complete forwarding. With the signature, Runtime generates an object anInvocation, sends it to the Front Invocation, and we have the Person object in the Front Invocation to execute the foo function. Signature parameter v@: How to explain, here the Apple document Type Encodings has a detailed explanation.
These are the three forwarding processes of Runtime. Now let’s talk about Runtime in action.
The Runtime application
Runtime is a great tool for building large frameworks. There are many application scenarios. The following are some common application scenarios.
- Objective-c Associated Objects add attributes to classes
- Method Swizzling Method add and replace and KVO implementation
- Message forwarding (hot update) Bug solved (JSPatch)
- Realize automatic archiving and automatic unarchiving of NSCoding
- Implement automatic dictionary and model transformations (MJExtension)
Objective-c Associated Objects add attributes to classes
We all know that categories cannot customize attributes and variables. Let’s add attributes to the category using the associated object implementation.
The associated object Runtime provides the following interfaces:
Void objc_setAssociatedObject(id object, const void *key, id value, Objc_getAssociatedObject (ID object, Const void *key) // remove associated objects void objc_removeAssociatedObjects(id object)Copy the code
Parameter interpretation
Id object: the associated object const void * Key: the associated key, which requires a unique ID Value: the associated object objc_AssociationPolicy Policy: the memory management policyCopy the code
Strategies for memory management
typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
OBJC_ASSOCIATION_ASSIGN = 0, /**< Specifies a weak reference to the associated object. */
OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /**< Specifies a strong reference to the associated object.
* The association is not made atomically. */
OBJC_ASSOCIATION_COPY_NONATOMIC = 3, /**< Specifies that the associated object is copied.
* The association is not made atomically. */
OBJC_ASSOCIATION_RETAIN = 01401, /**< Specifies a strong reference to the associated object.
* The association is made atomically. */
OBJC_ASSOCIATION_COPY = 01403 /**< Specifies that the associated object is copied.
* The association is made atomically. */
};
Copy the code
So let’s implement a UIView Category and add a custom property defaultColor.
#import "ViewController.h"
#import "objc/runtime.h"
@interface UIView (DefaultColor)
@property (nonatomic, strong) UIColor *defaultColor;
@end
@implementation UIView (DefaultColor)
@dynamic defaultColor;
static char kDefaultColorKey;
- (void)setDefaultColor:(UIColor *)defaultColor {
objc_setAssociatedObject(self, &kDefaultColorKey, defaultColor, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (id)defaultColor {
return objc_getAssociatedObject(self, &kDefaultColorKey);
}
@end
@interface ViewController ()
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
UIView *test = [UIView new];
test.defaultColor = [UIColor blackColor];
NSLog(@"% @", test.defaultColor);
}
@end
Copy the code
2018-04-01 15:41:44.977732+0800 OCram [2053:63739] UIExtendedGrayColorSpace 01
In print, we’ve added a property to the class that implements its setter and getter methods. The memory management of properties implemented by associated objects is also managed by ARC, so we only need to give the appropriate memory policy and do not need to worry about object release.
Let’s look at the memory measurement for attribute modification.
Memory strategy | Properties of modified | describe |
---|---|---|
OBJC_ASSOCIATION_ASSIGN | @property (assign) or @Property (unsafe_unretained) | Specifies a weak reference to an associated object. |
OBJC_ASSOCIATION_RETAIN_NONATOMIC | @property (nonatomic, strong) | @property (nonatomic, strong) specifies a strong reference to an associated object that cannot be used by atomization. |
OBJC_ASSOCIATION_COPY_NONATOMIC | @property (nonatomic, copy) | Specifies a copy reference to an associated object that cannot be used by atomization. |
OBJC_ASSOCIATION_RETAIN | @property (atomic, strong) | Specifies a strong reference to an associated object that can be used by atomization. |
OBJC_ASSOCIATION_COPY | @property (atomic, copy) | Specifies a copy reference to an associated object that can be used by atomization. |
Method Swizzling Method add and replace and KVO implementation
Method to add
Actually adding methods was mentioned earlier when we talked about message forwarding, dynamic method parsing.
//class_addMethod(Class _Nullable __unsafe_unretained cls, SEL _Nonnull name, IMP _Nonnull imp, const char * _Nullable types)
class_addMethod([self class], sel, (IMP)fooMethod, "v@:");
Copy the code
- CLS Class to which the method is added
- Name SEL of the name of the added method
- Imp method implementation. This function must take at least two arguments, self and _cmd
- Type encoding
Methods to replace
Here’s an example of implementing the viewDidLoad method instead of the ViewController.
@implementation ViewController
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
Class class = [self class];
SEL originalSelector = @selector(viewDidLoad);
SEL swizzledSelector = @selector(jkviewDidLoad);
Method originalMethod = class_getInstanceMethod(class,originalSelector);
Method swizzledMethod = class_getInstanceMethod(class,swizzledSelector);
//judge the method named swizzledMethod is already existed.
BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
// if swizzledMethod is already existed.
if (didAddMethod) {
class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
}
else{ method_exchangeImplementations(originalMethod, swizzledMethod); }}); } - (void)jkviewDidLoad { NSLog(@"Alternative methods");
[self jkviewDidLoad];
}
- (void)viewDidLoad {
NSLog(@"Bring your own way");
[super viewDidLoad];
}
@end
Copy the code
Swizzling should only be done in + Load. In the Objective-C runtime, each class has two methods that are automatically called. +load is called when a class is initially loaded, and +initialize is called before the application first calls a class method or instance method of that class. Both methods are optional and are called only if the method is implemented.
Swizzling should only be done in dispatch_once, since swizzling changes the global state, we need to make sure that every precaution is available at run time. Atomic operations are one such precaution to ensure that code is executed only once, even in different threads. Grand Central Dispatch’s dispatch_once satisfies the required requirements and should be used as the standard for using Swizzling’s initialization singleton method.
The implementation diagram is shown below.
As you can see from the figure, we swapped selectorC’s IMPc with selectorN’s IMPn using the Swizzling feature, so when we call selectorC, which is sending a selectorC message to an object, The corresponding method implementation is found to be IMPn, not IMPc.
KVO implementation
The full name is key-value observing, which translates to key-value observing. Provides a mechanism to notify an object when other object properties have been modified. In Cocoa, where MVC is popular, THE KVO mechanism is a great way to communicate between the Model and controller classes.
The implementation of KVO relies on objective-C’s powerful Runtime. When observing an object A, KVO dynamically creates A subclass of the object A’s current class and overrides the setter method for the observed property keyPath for the new subclass. The setter method is then responsible for notifying the observer of changes in the properties of the object.
Apple uses ISA-Swizzling to implement the KVO. When observing object A, the KVO mechanism dynamically creates A new one named: New class of NSKVONotifying_A, which inherits from this class of object A, and KVO for NSKVONotifying_A overrides the setter for the observed property, which takes care of calling the original setter before and after the original setter, Notifies all observation objects of changes in property values.
- NSKVONotifying_A class analysis
NSLog(@"self->isa:%@",self->isa);
NSLog(@"self class:%@",[self class]);
Copy the code
Before setting up the KVO listener, print the following:
self->isa:A
self class:A
Copy the code
After setting up the KVO listen, the print result is:
self->isa:NSKVONotifying_A
self class:A
Copy the code
In this process, the isa pointer of the observed object is changed from pointing to the original class A by the KVO mechanism to pointing to the system’s newly created subclass NSKVONotifying_A to implement the monitoring of the changes in the value of the property of the current class. So when we look at it from the application level, we are completely unaware of the new class, because the system “hides” the underlying implementation process of KVO, and we are mistaken for the original class. However, if we create a new class named “NSKVONotifying_A”, we will find that the system crashes when it registers KVO, because the system dynamically creates an intermediate class named NSKVONotifying_A and points to it.
- Subclass setter method profiling
KVO’s key-value observation notifications depend on two methods of NSObject :willChangeValueForKey: and didChangeValueForKey: WillChangeValueForKey: willChangeValueForKey: willChangeValueForKey: willChangeValueForKey: willChangeValueForKey: willChangeValueForKey: willChangeValueForKey: willChangeValueForKey: willChangeValueForKey: willChangeValueForKey; When the change is made, didChangeValueForKey: is called to notify the system that the value of the keyPath property has changed; After observeValueForKey: ofObject: change: context: will be invoked. And overriding the setter method of the observation property is an inherited injection that is implemented at run time, not compile time.
How KVO overrides calling access methods for the observer properties of subclasses works in code is equivalent to:
- (void)setName:(NSString *)newName {
[self willChangeValueForKey:@"name"]; //KVO always calls [super] before calling the access methodsetValue:newName forKey:@"name"]; // Call the parent class access method [self didChangeValueForKey:@"name"]; //KVO is called after the access method is called}Copy the code
Message forwarding (hot update) Bug solved (JSPatch)
JSPatch is an iOS dynamic update framework that allows you to use JavaScript to invoke any native objective-C interface by simply introducing a minimal engine into your project, gaining the advantages of scripting languages: dynamically adding modules to your project, or dynamically fixing bugs by replacing the project’s native code.
As mentioned earlier, message forwarding is divided into three levels, and we can implement replacement function at each level to achieve message forwarding without crashing. JSPatch can not only forward messages, but also implement a series of functions such as method adding and replacing.
Realize automatic archiving and automatic unarchiving of NSCoding
Principle description: Use runtime functions to traverse all properties of the Model itself, and conduct encode and decode operations on the properties. Core methods: Override methods in Model’s base class:
- (id)initWithCoder:(NSCoder *)aDecoder {
if (self = [super init]) {
unsigned int outCount;
Ivar * ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i ++) {
Ivar ivar = ivars[i];
NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[self setValue:[aDecoder decodeObjectForKey:key] forKey:key]; }}return self;
}
- (void)encodeWithCoder:(NSCoder *)aCoder {
unsigned int outCount;
Ivar * ivars = class_copyIvarList([self class], &outCount);
for (int i = 0; i < outCount; i ++) {
Ivar ivar = ivars[i];
NSString * key = [NSString stringWithUTF8String:ivar_getName(ivar)];
[aCoder encodeObject:[self valueForKey:key] forKey:key]; }}Copy the code
Implement automatic dictionary and model transformations (MJExtension)
How it works: Use the runtime function to iterate over all the properties of the Model itself, and assign a value to the property if it has a corresponding value in json. Core methods: Add methods to the classification of NSObject
- (instancetype)initWithDict:(NSDictionary *)dict {
if(self = [self init]) {NSMutableArray * keys = [NSMutableArray Array]; NSMutableArray * keys = [NSMutableArray Array]; NSMutableArray * attributes = [NSMutableArray array]; /* * Example * name = value3 attribute = t@"NSString",C,N,V_value3
* name = value4 attribute = T^i,N,V_value4
*/
unsigned int outCount;
objc_property_t * properties = class_copyPropertyList([self class], &outCount);
for(int i = 0; i < outCount; i ++) { objc_property_t property = properties[i]; / / get the name of the property by property_getName function nsstrings * propertyName = [nsstrings stringWithCString: property_getName (property) encoding:NSUTF8StringEncoding]; [keys addObject:propertyName]; // The property_getAttributes function gives you the name of the property and @encode NSString * propertyAttribute = [NSString] stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding]; [attributes addObject:propertyAttribute]; } // Immediately release the memory pointed to by properties. Free (properties); //(2) Assign a value to an attribute based on its typefor (NSString * key in keys) {
if ([dict valueForKey:key] == nil) continue;
[self setValue:[dict valueForKey:key] forKey:key]; }}return self;
}
Copy the code
Those are some of the scenarios where Runtime is applied, and that’s the end of this article.
Pay attention to my
Welcome to follow the public account: Jackyshan, technology dry goods first send wechat, the first time push.