Welcome to my blog post

Runtime is the deferment of data type determination from compile time to Runtime. It’s a set of underlying pure C apis, and all the objective-C code that we write, we end up converting to C code for the Runtime.

However, the implementation of the runtime API is developed in C++ (the implementation files in the source are.mm files).

To get a fuller understanding of the Runtime mechanism, let’s use the latest objC4 source code.

The messaging

We know objective-C is object oriented and C is process oriented, so we need to turn object-oriented classes into process oriented structures.

In Objective-C, all “messages” in messaging are converted by the compiler to:

id objc_msgSend ( id self, SEL op, ... ) ;Copy the code

For example, the method of executing an object: [obj foo]; , the underlying runtime is converted by the compiler to objc_msgSend(obj, @selector(foo)); .

So what does the execution flow look like inside a method? Let me start with some concepts.

concept

objc_object

An Objective-C object is represented by an ID type, which is essentially a pointer to an objc_Object structure.

typedef struct objc_object *id;

union isa_t {
    isa_t() {}isa_t(uintptr_t value) : bits(value) { }

    Class cls;
    uintptr_t bits;
#if defined(ISA_BITFIELD)
    struct {
        ISA_BITFIELD;  // defined in isa.h
    };
#endif
};

struct objc_object {
private:
    isa_t isa;
// public & private method...
}
Copy the code

We see that objc_Object has only one object in its structure, which is the ISA pointer to its class.

When sending a message to an object, the Runtime finds the class to which the instance object belongs based on the ISA pointer.

objc_class

Objective-c classes are represented by the Class type, which is actually a pointer to the objc_class structure.

typedef struct objc_class *Class;
Copy the code

The objc_class structure defines a number of variables:

struct objc_class : objc_object {
    // Pointer to class (in objc_object)
    // Class ISA;
    // A pointer to the parent class
    Class superclass;
    // Caches Pointers and vtables to speed up method calls
    cache_t cache;             // formerly cache pointer and vtable
    // A place to store information about a class's methods, attributes, protocols that it follows, and so on
    class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    // the class_data_bits_t constructor method used to return the class_rw_t pointer ()
    class_rw_t *data() { 
        return bits.data();
    }
    // other methods...
}

struct class_rw_t {
    // Be warned that Symbolication knows the layout of this structure.
    uint32_t flags;
    uint32_t version;

    const class_ro_t *ro;
    
    method_array_t methods;
    property_array_t properties;
    protocol_array_t protocols;
    
    Class firstSubclass;
    Class nextSiblingClass;
    
    char *demangledName;

#if SUPPORT_INDEXED_ISA
    uint32_t index;
#endif
    // other methods
}
Copy the code

Objc_class inherits from Objc_Object, so it also has an ISA pointer. In addition, its structure holds Pointers to the parent class, caches, a list of instance variables, a list of methods, protocols to follow, and so on.

The metaclass

Metaclass is aclass of class objects that has the same structure as objc_class.

Since all classes are themselves an object, we can send messages to this object, such as calling class methods. In order to call a class method, the isa pointer to that class must point to an objC_class structure that contains the class method. Class objects store only instance methods, but no class methods, which leads to the concept of metaclasses, which hold all the information needed to create class objects and class methods.

To make it easier to understand, here’s an example:

- (void)eat; // an instance method + (void)sleep; // a class method // then the instance method needs to be called by the class object: [person eat]; // The class method needs to be called by the metaclass: [Person sleep];Copy the code

If the person object can also call sleep, then we can’t tell if it calls + (void)sleep; Or – (void) sleep; .

Class objects are instances of metaclasses, of class objectsisaThe pointer points to the metaclass.

This may be a little convoluted, but use this classic picture to understand:

When sending a message to an object, Runtime looks for the corresponding method in the list of methods of that object’s class, but when sending a message to a class, Runtime looks for the corresponding method in the list of meta Class methods of that class. The ISA of all meta classes, including Root class, Superclass, and Subclass, points to the meta class of the Root class, thus forming a closed loop.

Method(method_t)

Method is a pointer to the method_t structure and is defined in objc-private.h and objc-runtime-new.h:

typedef struct method_t *Method;
Copy the code
struct method_t {
    // Method selector
    SEL name;
    // Type encoding
    const char *types;
    // A pointer to the method implementation
    MethodListIMP imp;
}
Copy the code

Method = SEL + IMP + types

For types, see Type Encodings.

SEL(objc_selector)

SEL, also known as method selector, is a pointer to the objc_selector structure and the second parameter type of the objc_msgSend function.

typedef struct objc_selector *SEL;
Copy the code

Method selector is used to represent the name of the runtime method. When the code is compiled, it generates a unique integer identifier (an address of type Int), SEL, based on the name of the method (excluding arguments).

There can’t be two identical SEL’s in a class’s list of methods, which is why Overloading is not supported in Objective-C.

Different classes can have the same SEL, because instance objects of different classes performing the same selector will look for their corresponding IMPs in their respective method lists.

There are three ways to obtain SEL:

  • sel_registerNamefunction
  • Provided by the Objective-C compiler@selector()methods
  • NSSeletorFromString()methods

IMP

An IMP is essentially a function pointer to the address of the method implementation.

typedef void (*IMP)(void /* id, SEL, ... * / ); 
Copy the code

Parameter Description:

  • Id: pointer to self (in the case of an instance method, the memory address of the class instance; A pointer to a metaclass if it is a class method.)
  • SEL: method selector
  • . : The argument list of the method

The relationship between SEL and IMP is similar to that between key and value in a hash table. Using this hash mapping method can speed up the method search.

cache_t

Cache_t represents the class cache and is one of the structural variables of object_class.

struct cache_t {
    // An array of methods
    struct bucket_t* _buckets;
    // The maximum number that can be stored
    mask_t _mask;
    // The number of methods currently stored
    mask_t _occupied;
    // ...
}
Copy the code

To speed up message distribution, methods and their corresponding addresses are cached in cache_t.

In practice, most commonly used methods are cached, and the Runtime system is actually very fast, close to the speed of a program that executes a memory address directly.

category_t

Category_t represents a pointer to the categorized structure.

struct category_t {
    // Indicates the class name, not the class name
    const char *name;
    // The class object to be extended is not defined at compile time, but corresponds to the corresponding class object by name at run time
    classref_t cls;
    // List of instance methods
    struct method_list_t *instanceMethods;
    // List of class methods
    struct method_list_t *classMethods;
    // Protocol list
    struct protocol_list_t *protocols;
    // Instance properties
    struct property_list_t *instanceProperties;
    // Fields below this point are not always present on disk.
    // Class (metaclass) property list
    struct property_list_t* _classProperties;
    method_list_t *methodsForMeta(bool isMeta) {
        if (isMeta) return classMethods;
        else return instanceMethods;
    }

    property_list_t *propertiesForMeta(bool isMeta, struct header_info *hi);
};
Copy the code

Here’s a classic question:

Can I add instance variables/member variables/attributes to a class?

First, instance and member variables cannot be added directly to a classification.

As a matter of practice, adding instance/member variables to a classification generates an error at compile time, but adding attributes is allowed.

This is because there is no “instance variable/member variable” structure in the classification structure, but there is an “attribute” structure.

Can you add attributes directly to a category?

In fact, this is not the case, although there is no error message in the.h classification, but the following warning is reported in the.m, and the runtime error.

The warning indicates that there are two solutions:

The first: use the @dynamic modifier. But really, the @dynamic modifier just tells the compiler that the setter and getter methods for the property will be implemented by the user. But doing so only eliminates warnings, not fixes the problem, and the runtime still crashes.

The second option: manually adding setters and getters to classes is an efficient one.

We know at sign property equals ivar plus setter plus getter.

You can dynamically add attributes to a category using objc_setAssociatedObject and objc_getAssociatedObject, as described in “Adding attributes to a category by associating Objects” below.

process

The complete process of message passing is as follows:

That is, the process of finding IMP:

  • First look for the current class’s cache method list.
  • If it does, it returns the corresponding IMP implementation and caches the selector in the current class.
  • If it’s not in the list of methods of the class, it looks in the list of methods of the parent class until it finds the NSObject class.
  • When it does not, it enters dynamic method resolution and message forwarding.

forward

If the IMP cannot be found after the message is delivered, the message forwarding process is entered.

  1. With dynamic method resolution at run time, we can add a method to a class when it is needed.
  2. An object can forward some selectors that it cannot decipher to the standby receiver for processing.
  3. After the above two steps, if there is still no way to process the selectors, start the full message forwarding mechanism.

Dynamic method parsing

Two methods for dynamic method resolution:

// Add class methods
+ (BOOL)resolveClassMethod:(SEL)sel OBJC_AVAILABLE(10.5.2.0.9.0.1.0.2.0);
// Add instance methods
+ (BOOL)resolveInstanceMethod:(SEL)sel OBJC_AVAILABLE(10.5.2.0.9.0.1.0.2.0);
Copy the code

Let’s look at these two methods called in the source code:

void _class_resolveMethod(Class cls, SEL sel, id inst)
{
    // check whether it is a metaclass
    if (! cls->isMetaClass()) {
        // try [cls resolveInstanceMethod:sel]
        // call the class resolveInstanceMethod method to dynamically add instance methods
        _class_resolveInstanceMethod(cls, sel, inst);
    } 
    else {
        // try [nonMetaClass resolveClassMethod:sel]
        // and [cls resolveInstanceMethod:sel]
        // Call the resolveClassMethod method of the metaclass to dynamically add class methods
        _class_resolveClassMethod(cls, sel, inst);
        if(! lookUpImpOrNil(cls, sel, inst, NO/*initialize*/, YES/*cache*/, NO/*resolver*/)) { _class_resolveInstanceMethod(cls, sel, inst); }}}Copy the code

Let’s look at an example of dynamic method resolution.

- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(foo)];
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(foo)) {
        class_addMethod([self class], sel, (IMP)fooMethod, "v@:");
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}

void fooMethod(id obj, SEL _cmd) {
    NSLog(@"Doing foo");
}
Copy the code

As you can see, foo is not implemented, but fooMethod is dynamically added via class_addMethod and fooMethod IMP is executed.

If resolveInstanceMethod: method to return NO, runtime will move to the next step: forwardingTargetForSelector:.

Alternate receiver

If the target object implements forwardingTargetForSelector: method, the runtime can call this method, give you put forward this message to other recipients.

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");// The foo function of Person
}

@end

@interface ViewController(a)

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(foo)];
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    // Return NO to proceed to the next step of forwarding.
    return NO;
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(foo)) {
        // Return the Person object to receive the message
        return [Person new];
    }
    return [super forwardingTargetForSelector:aSelector];
}

@end
Copy the code

The implementation of the above is the use of the current forwardingTargetForSelector ViewController method of class foo forwarded to spare the recipient to perform the Person class.

Complete message forwarding

If you could not handle unknown messages in the previous step, the only thing you can do is enable a full message forwarding mechanism.

There are two main methods involved:

  • sendmethodSignatureForSelectorPerforms method signature, which encapsulates the parameter types and return values of the function. If it returns nil, the Runtime will emitdoesNotRecognizeSelectorMessage and program crash simultaneously.
  • If a function signature is returned, the Runtime creates oneNSInvocationObject and sendforwardInvocationMessage to the target object.

An example of implementing a complete forward is as follows:

#import "ViewController.h"
#import <objc/runtime.h>

@interface Person: NSObject

@end

@implementation Person

- (void)foo {
    NSLog(@"Doing foo");
}

@end


@interface ViewController(a)

@end

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    [self performSelector:@selector(foo)];
}

+ (BOOL)resolveInstanceMethod:(SEL)sel {
    // Return NO to proceed to the next step of forwarding.
    return NO;
}

- (id)forwardingTargetForSelector:(SEL)aSelector {
    // Return nil to proceed to the next forward.
    return nil;
}

- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if ([NSStringFromSelector(aSelector) isEqualToString:@"foo"]) {
        return [NSMethodSignature signatureWithObjCTypes:"v@:"];// sign, 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{[selfdoesNotRecognizeSelector:sel]; }}@end
Copy the code

With the signature, the runtime generates an object, the anInvocation, to the method forwardInvocation, where we have the Person object execute the foo function.

The above is the three-way forwarding process of Runtime. The following are the practical applications of Runtime.

application

The associated object adds attributes to the classification

Associated Objects are a feature of the Objective-C runtime that allows developers to add custom properties to existing classes in extensions.

The associated object Runtime provides three apis:

// Get the associated object ID objc_getAssociatedObject(id object, const void *key); Void objc_setAssociatedObject(id object, const void *key, ID value, objc_AssociationPolicy policy); Void objc_removeAssociatedObjects(id object); // Remove the associated object.Copy the code

Parameter Description:

  • object: Indicates the associated object
  • key: Unique identifier of the associated object
  • value: Associated object
  • policy: Memory management policy

The runtime.h source code describes the memory management strategy as follows:

/* Associative References */

/** * Policies related to associative references. * These are options to objc_setAssociatedObject() */
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

Let’s look at the attribute modifier corresponding to the memory policy.

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) Specifies a strong reference to an associated object that cannot be used atomically.
OBJC_ASSOCIATION_COPY_NONATOMIC @property (nonatomic, copy) Specifies a copy reference to an associated object that cannot be used atomically.
OBJC_ASSOCIATION_RETAIN @property (atomic, strong) Specifies a strong reference to an associated object that can be used atomically.
OBJC_ASSOCIATION_COPY @property (atomic, copy) Specifies a copy reference to an associated object that can be used atomically.

Add a non-atomic property prop modified with copy to a class using associative objects.

We already know that you can’t add attributes to a class directly, you need to manually add access methods:

// NSObject+AssociatedObject.h

#import <Foundation/Foundation.h>

@interface NSObject (AssociatedObject)

@property (nonatomic.copy) NSString *prop;

@end

// NSObject+AssociatedObject.m

#import "NSObject+AssociatedObject.h"
#import <objc/runtime.h>

// There are three common ways to write key:
//
// 1. static void *propKey = &propKey;
// 2. static NSString *propKey = @"propKey";
// 3. static char propKey;

static NSString *propKey = @"propKey";

@implementation NSObject (AssociatedObject)

- (void)setProp:(NSString *)prop {
    objc_setAssociatedObject(self, &propKey, prop, OBJC_ASSOCIATION_COPY_NONATOMIC);
}

- (NSString *)prop {
    return objc_getAssociatedObject(self, &propKey);
}

@end
Copy the code

Dark magic add and replace methods

The dark magic is method swizzling, which is the IMP implementation of swapping methods.

It’s usually + (void)load; Perform method exchange in. Because it loads early, it basically ensures that methods are swapped.

Method to add

“Method addition” is already mentioned in dynamic method resolution.

//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

Parameter Description:

  • cls: The class to which the method is added
  • nameSEL to add the method name
  • imp: method implementation. This function must take at least two arguments, self,_cmd
  • types: Type encoding

Methods to replace

Method substitution is to change the selection submapping table of the class.

If you want to interchange two already written method implementations, you can use the following function

void method_exchangeImplementations(Method m1, Method m2);
Copy the code

Method implementations can be obtained by using the following function:

void class_getInstanceMethod(Class aClass, SEL aSelector);
Copy the code

Let’s implement an example to replace the viewDidLoad method in ViewController.

@implementation ViewController
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        Class class = [self class];
        SEL originalSelector = @selector(viewDidLoad);
        SEL swizzledSelector = @selector(msviewDidLoad);
        
        Method originalMethod = class_getInstanceMethod(class,originalSelector);
        Method swizzledMethod = class_getInstanceMethod(class,swizzledSelector);
        
        // Determine whether original's method is implemented. If not, add the implementation and type of swizzledMethod to originalSelector
        BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod));
        if (didAddMethod) {
            // Replace the originalMethod implementation and type with swizzledSelector
            class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod));
        }
        else {
            // Swap originalMethod and swizzledMethodmethod_exchangeImplementations(originalMethod, swizzledMethod); }}); } - (void)msviewDidLoad {
    NSLog(@"msviewDidLoad");
    [self msviewDidLoad];
}

- (void)viewDidLoad {
    NSLog(@"viewDidLoad");
    [super viewDidLoad];
}
@end
Copy the code

KVO implementation

The full name of KVO is key-value observing, which is also the key-value observer mode. It provides a mechanism for notifying the current object when the properties of other objects are modified.

The implementation of KVO also relies on isa-Swizzling in Runtime.

When observing object A, the KVO mechanism dynamically creates A new object named: NSKVONotifying_A is A new class that inherits from object A, and KVO is NSKVONotifying_A that overrides the setter method to observe the property, and the setter method is responsible for calling the original setter method before and after, Notifies all observed objects of changes in property values.

Here’s an example:

#import "ViewController.h"
#import <objc/runtime.h>
#import "A.h"

@implementation ViewController

- (void)viewDidLoad {
    [super viewDidLoad];
    A *a = [A new];
    NSLog(@"Before KVO: [a class] = %@, a -> isa = %@", [a class], object_getClass(a));
    [a addObserver:self forKeyPath:@"name" options:NSKeyValueObservingOptionNew context:nil];
    NSLog(@"After KVO: [a class] = %@, a -> isa = %@", [a class], object_getClass(a));
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey.id> *)change context:(void *)context {
}

@end
Copy the code

The program results are as follows:

Before KVO: [a class] = A, a -> isa = A
After KVO: [a class] = A, a -> isa = NSKVONotifying_A
Copy the code

Isa refers to NSKVONotifying_A, a subclass of NSKVONotifying_A, a subclass of NSKVONotifying_A.

So when we look at the application level, we are completely unaware that there is a new class, this is the system “hiding” the underlying implementation of KVO, making us think it is the original class. However, if we create a new class named NSKVONotifying_A, we will find that the system will crash when we run the code to register KVO, because the system will dynamically create a middle class named NSKVONotifying_A when we register listening, and point to this middle class.

So what does subclass NSKVONotifying_A implement in setter methods?

KVO’s key-value observation notification relies on NSObject’s two methods:

  • -willChangeValueForKey: : This method is called before the observed property changes, notifying the system that the keyPath property value is about to change;

  • -didChangeValueForKey: : This method is called after the observed property has changed, notifying the system that the keyPath property value has changed. Methods observeValueForKey: ofObject: change: context: will also be called. And override setter methods for observing properties. This inheritance injection is implemented at run time, not compile time.

Thus, KVO calls the access method for the observer property override of a subclass and works in code equivalent to:

- (void)setName:(NSString *)name {
    // KVO always calls before calling the accessor method
    [self willChangeValueForKey:@"name"];
    // Call the accessor method of the parent class
    [super setValue:newName forKey:@"name"];
    // KVO always calls after calling the accessor method
    [self didChangeValueForKey:@"name"];
}
Copy the code

Implement transformations between dictionaries and models (MJExtension)

Principle:

By adding the method -initWithDict: to the class of NSObject.

Concrete implementation is as follows: Use the runtime function class_copyPropertyList to get the property list, and then iterate over all the properties of the Model itself (property_getName to get the property name, Get the type of the property from the property_getAttributes function). If the property has a corresponding value in JSON, it is assigned.

Source:

- (instancetype)initWithDict:(NSDictionary *)dict {
    if (self = [self init]) {
        // get the class attribute and its corresponding type
        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 using the property_getName function
            NSString * propertyName = [NSString stringWithCString:property_getName(property) encoding:NSUTF8StringEncoding];
            [keys addObject:propertyName];
            // Get the property type using the property_getAttributes function
            NSString * propertyAttribute = [NSString stringWithCString:property_getAttributes(property) encoding:NSUTF8StringEncoding];
            [attributes addObject:propertyAttribute];
        }
        // Immediately free the memory pointed to by properties
        free(properties);

        // 2. Assign attributes based on type
        for (NSString * key in keys) {
            if ([dict valueForKey:key] == nil) continue;
            [selfsetValue:[dict valueForKey:key] forKey:key]; }}return self;
}
Copy the code

NSCoding automatic archiving and filing

Principle:

Override the methods -initWithcoder: and -encodeWithcoder: in the base class of Model.

The specific implementation is as follows: use the function class_copyIvarList provided by Runtime to obtain the list of instance variables, traverse all attributes of Model itself, and encode and decode the attributes.

Source:

- (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)];
            [selfsetValue:[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:[selfvalueForKey:key] forKey:key]; }}Copy the code

JSPatch

JSPatch is an iOS dynamic update framework that allows hot updates using JavaScript to invoke all objective-C native interfaces, simply by introducing an engine into your project.

It implements the problem of obtaining parameters through full message forwarding.

Principle:

When you call a method that doesn’t have an NSObject object, it doesn’t throw an exception right away. Instead, it goes through multiple layers of forwarding, Layers of invocation object – resolveInstanceMethod:, – forwardingTargetForSelector:, – methodSignatureForSelector:, – forwardInvocation: The NSInvocation object in the -Forward Invocation saves all information about the method invocation, including the method name, parameters, and return value types. This will not happen if the JS invocation is called to the -Forward Invocation:.