Break down

Statically typed versus dynamically typed languages

  • Statically typed languages: languages in which the data type of a variable is determined at compile time. Most statically typed languages require that the variable’s type be declared before it can be used. The call to a function determines which function is called at compile time. C, C++, Java, and C# are statically typed languages.

  • Dynamically typed language: A language in which the data type of a variable is determined at run time. Variables do not require a variable type declaration before being used. The usual variable type is the type of the variable to which they are assigned. Python, Ruby, OC, JS are all dynamically typed languages.

OC is a dynamic language that requires not only a compiler, but also a runtime system. The most important factor behind OC is runtime, which is written based on C and hybrid programming. At compile time, it is not possible to determine which function to call. Only when the function is actually run, it will find the corresponding function to call according to its name.

Runtime related header files

There are several files under the usr/include/objc folderH and runtime.h files. Message. h contains functions that send messages to objects, which is the underlying implementation of OC object method calls. Runtime.h is the run-time’s most important file, which contains the methods that operate on the runtime. To use it, you need to import files, such as:

#import <objc/runtime.h>
#import <objc/message.h>
Copy the code

Object, class, and method structures:

object

Its ISA pointer points to a class object,

struct objc_object {
    
    Class _Nonnull isa  OBJC_ISA_AVAILABILITY;
};
Copy the code

class

It is actually a pointer to the objC_class structure. The first member variable is also an ISA pointer, which shows that Class itself is also an object, so we call it a Class object. Class objects are created at compile time to create instance objects, and are singletons.

Struct objc_class {// 'isa' pointer to a metaclass (' metaclass 'holds all the information needed to create aclass object and its methods); Class _Nonnull isa OBJC_ISA_AVAILABILITY; #if ! __OBJC2__ // The superclass pointer points to the superclass object of the superclass, and the super_class pointer points to the superclass of the superclass. Class _Nullable super_class OBJC2_UNAVAILABLE; // Class name const char * _Nonnull name OBJC2_UNAVAILABLE; // version long version OBJC2_UNAVAILABLE; long info OBJC2_UNAVAILABLE; // Instance size long instance_size OBJC2_UNAVAILABLE; Struct objc_iVAR_list * _Nullable ivars OBJC2_UNAVAILABLE; Struct objc_method_list * _Nullable * _Nullable methodLists OBJC2_UNAVAILABLE; // Cache struct objc_cache * _Nonnull Cache OBJC2_UNAVAILABLE; Struct objc_PROTOCOL_list * _Nullable protocols OBJC2_UNAVAILABLE; #endif } OBJC2_UNAVAILABLE; #### method list struct objc_method_list {struct objc_method_list * _Nullable 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; } #### method struct objc_method {// method name SEL _Nonnull method_name OBJC2_UNAVAILABLE; // Method type char * _Nullable method_types OBJC2_UNAVAILABLE; // methods implement IMP _Nonnull method_IMP OBJC2_UNAVAILABLE; }Copy the code

Definition of a function

  • The general way to operate on objects is as followsobject_At the beginning
  • The general way to operate on a class is as followsclass_At the beginning
  • Methods that operate on methods of a class or object are generally defined asmethod_At the beginning
  • The general way to operate on a member variable is as followsivar_At the beginning
  • The general way to operate on attributes is as followsProperty_ beginningAt the beginning
  • The operation method of the protocol is generally as followsprotocol_At the beginning

Class cache (objc_cache)

When the Objective-C runtime examines an object by tracing its ISA pointer, it can find an object that implements many methods. However, you may only call a small number of them, and it doesn’t make sense to search the class dispatch table for all selectors each time you look up. So the class implements a cache, and whenever you search a class dispatch table and find the corresponding selector, it puts it into 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. To speed up message distribution, the system caches methods and their addresses in objc_cache, so in practice most commonly used methods are cached. The Runtime system is actually very fast, approaching the speed of programs that directly execute memory addresses.

Classification (objc_category)

From the definition, categories can add instanceMethods (instanceMethods), classMethods (classMethods), protocols, and instanceProperties. Do not: You cannot add instance variables

Although a category can add the instance attribute @Property, it does not generate a new instance variable, nor does it generate the corresponding setter and getter methods. Even if it’s added, it doesn’t really work. There is no problem with the call at compile time, and the compiler does not report errors; But it crashes at runtime. Because I couldn’t find the getter/setter method. If you want to use it, you need to associate the object implementation. The associated object does not automatically generate new instance variables. This is because the clAS Ivarlist and IvarLayout sizes are determined by the compiler and cannot be changed.

A Category is a pointer to a classification structure and is defined as follows:

Struct category_t {// category_name class_name not category_name. const char *name; // The class object to be extended is not defined at compile time, but is referred to the corresponding class object by the name pair at Runtime. 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

Usage scenarios

Runtime Message Forwarding

When we call a method, which is essentially sending a message to the object, Runtime searches the list of methods in the relevant class object, and if it doesn’t find any, it searches up the inheritance tree until it reaches the root of the inheritance tree (usually NSObject), and if it doesn’t find any, Runtime gives three final fixes. If you don’t do a remediation you’re going to get an unrecognized selector error

  • Dynamic method parsing
  • Specify the object to execute
  • The message is redirected, and the message is forwarded

The Runtime application

The Runtime is a great tool for building large frameworks. There are many application scenarios, and the following are some common ones.

  • Objective-c Associated Objects add attributes to a classification
@property (nonatomic, strong, nullable) UIImage                   *mt_emptyDataImag;
static char const * const kEmptyDataImag             =       "mt_emptyDataImag";
- (UIImage *)mt_emptyDataImag
{
    return objc_getAssociatedObject(self, kEmptyDataImag);
}
- (void)setMt_emptyDataImag:(UIImage *)mt_emptyDataImag
{
    objc_setAssociatedObject(self, kEmptyDataImag, mt_emptyDataImag, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
Copy the code
  • Black magic (Method Swizzling Method replacement (NSArray out of bounds processing,URL escape processing,VC full screen present processing, buried point, etc.)
@implementation UIViewController (FullModal) + (void)load{ SEL originalSel = @selector(presentViewController:animated:completion:); SEL overrideSel = @selector(override_presentViewController:animated:completion:); Method originalMet = class_getInstanceMethod(self.class, originalSel); Method overrideMet = class_getInstanceMethod(self.class, overrideSel); method_exchangeImplementations(originalMet, overrideMet); } #pragma mark - Swizzling - (void)override_presentViewController:(UIViewController *)viewControllerToPresent animated: (BOOL)flag completion (void (^ __nullable)(void))completion{if(@available(iOS 13.0, *)){ if (viewControllerToPresent.modalPresentationStyle == UIModalPresentationPageSheet){ viewControllerToPresent.modalPresentationStyle = UIModalPresentationFullScreen; } } [self override_presentViewController:viewControllerToPresent animated:flag completion:completion]; }Copy the code
  • Message forwarding process Crash
/ / dynamic parsing + (BOOL) resolveInstanceMethod: (SEL) SEL / / specified object - (id) forwardingTargetForSelector: (SEL) forward aSelector / / redirection - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector - (void)forwardInvocation:(NSInvocation *)anInvocationCopy the code
  • NSCoding automatic archiving and automatic file solution
- (id)initWithCoder:(NSCoder *)aDecoder
{
    if (self = [super init]) {
        unsigned int count = 0;
        Ivar *ivars = class_copyIvarList([self class], &count);
        for (int i = 0; i < count; i++) {
            Ivar ivar = ivars[i];
            const char *name = ivar_getName(ivar);
            NSString *key = [NSString stringWithUTF8String:name];
            id value = [aDecoder decodeObjectForKey:key];
            [self setValue:value forKey:key];
        }
       free(ivars);
    }
    return self;
}

- (void)encodeWithCoder:(NSCoder *)aCoder
{
    unsigned int count = 0;
    Ivar *ivars = class_copyIvarList([self class], &count);
    for (int i = 0; i < count; i++) {
        Ivar ivar = ivars[i];
        const char *name = ivar_getName(ivar);
        NSString *key = [NSString stringWithUTF8String:name];
        id value = [self valueForKey:key];
        [aCoder encodeObject:value forKey:key];
    }
    free(ivars);
}
Copy the code
  • Implement automatic transformation of dictionaries and models (MJExtension)