底层探索--runtime的本质

2021-10-14  本文已影响0人  永断阎罗

基本

涉及基础

位运算详解

位运算对应isa指针的实际应用

例如isa位域的操作:

实例:
union Test { //共用体:开辟所有属性中占用最大字节的内存,每个属性使用共同的空间,值会覆写。
    char bits; //(char内存中占一个字节,占8位)
    struct { //相当于说明,占4位
        char isMan : 1;
        char isDead : 1;
        char age : 2;
    };
} test;

#define kIsMan (1 << 0)

//设置对应isMan的值,而其他位不变
- (void)setIsMan:(BOOL)isMan {
    if (isMan) {
        test.bits |= kIsMan;
    }else {
        test.bits &= (~kIsMan);
    }
}

//取出对应isMan的值
- (BOOL)isMan {
    return !!(test.bits & kIsMan); //保证取出的值为BOOL值
}

方法本质

消息转发

参考文章

[[MessageSend new] sendMessage:@"Hello"];
 //等同于
 objc_msgSend([MessageSend new], @selector(sendMessage:), @"Hello");

步骤:

  1. 首先通过[Message new]对象的isa指针找打它对应的class。
  2. 在class的cache list 查找是否有sendMessage方法,有则返回,否则往下走。
  3. 在class的method list 查找是否有sendMessage方法。
  4. 如果没有就去它的superclass里继续查找(先cache、再method list)。
  5. 一旦查找到对应的函数方法,就去执行它的实现IMP。
  6. 如果一直没有找到,就执行消息转发。
  7. 消息转发机制:
    1. 动态方法解析:为指定方法(sel)添加方法实现(IMP)

       +(BOOL)resolveInstanceMethod:(SEL)sel; //对象方法-动态解析
       +(BOOL)resolveClassMethod:(SEL)sel; //类方法-动态解析
      
    2. 快速转发:为方法指定备援的接受者(即类对象)

        - (id)forwardingTargetForSelector:(SEL)aSelector; //针对对象方法
        + (id)forwardingTargetForSelector:(SEL)aSelector; //针对类方法
      
    3. 慢速转发:手动生成方法签名并转发给另一对象,分两步(方法签名+指定备胎对象)

       //针对对象方法
       - (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector;
       - (void)forwardInvocation:(NSInvocation *)anInvocation;
       //针对类方法
       + (NSMethodSignature*)methodSignatureForSelector:(SEL)aSelector;
       + (void)forwardInvocation:(NSInvocation *)anInvocation;
      
      runtime之方法编码表.png
    4. 消息未处理:避免崩溃

         - (void)doesNotRecognizeSelector:(SEL)aSelector; //针对对象方法
         + (void)doesNotRecognizeSelector:(SEL)aSelector; //针对类方法
      

注:越往下花费的代价越大。
重点:对象方法和类方法传递的对象不同,一个传递类对象,一个传递元类对象。!!!!

super本质

//objc_super2在最新官方源码中的结构体定义
struct objc_super2 {
    id receiver; //接受者
    Class current_class; //当前类,内部实现是会取它的superclass
};

//作用:从消息接受者的superclass类开始找对象方法或类方法
(void *)objc_msgSendSuper2)({
   (id)self,
   (id)class_getSuperclass(objc_getClass("Student"))
}, sel_registerName("class"))

//sel_registerName("class") == @selector(class)
//结构体-两个成员:{self 和 class_getSuperclass("Student") == 父类}



+ (Class)class {
    return self;
}

- (Class)class {
    return object_getClass(self);
}

+ (Class)superclass {
    return self->superclass;
}

- (Class)superclass {
    return [self class]->superclass;
}


//探究:super的本质
NSLog(@"[self class] %@\n",[self class]);
NSLog(@"[self superclass] %@\n",[self superclass]);
NSLog(@"[super class] %@\n",[super class]);
NSLog(@"[super superclass] %@\n",[super superclass]);

/**
 //打印结果:
 testApp[1650:33448] [self class]       Student
 testApp[1650:33448] [self superclass]  Person
 testApp[1650:33448] [super class]      Student
 testApp[1650:33448] [super superclass] Person
*/

//解释:接受者为self,方法为class,然class是NSObject的对象方法,而class内部代码为“object_getClass(self)”故在这种情况下:==[self class]

探究isMemberOfClassandisKindOfClass

Runtime部分常用方法介绍

方法交换实际应用

//方法事件点击:---- UIControl分类
+ (void)load
{
    // hook:钩子函数
    Method method1 = class_getInstanceMethod(self, @selector(sendAction:to:forEvent:));
    Method method2 = class_getInstanceMethod(self, @selector(mj_sendAction:to:forEvent:));
    method_exchangeImplementations(method1, method2);
}

//拦截了所有点击的事件
- (void)mj_sendAction:(SEL)action to:(id)target forEvent:(UIEvent *)event
{
    NSLog(@"%@-%@-%@", self, target, NSStringFromSelector(action));
    
    // 调用系统原来的实现
    [self mj_sendAction:action to:target forEvent:event];
}

//拦击数组所有添加、插入的事件:---- NSMutableArray分类
+ (void)load {
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        // 类簇:NSString、NSArray、NSDictionary,真实类型是其他类型
        Class cls = NSClassFromString(@"__NSArrayM");
        Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
        Method method2 = class_getInstanceMethod(cls, @selector(mj_insertObject:atIndex:));
        method_exchangeImplementations(method1, method2);
    });
}

- (void)mj_insertObject:(id)anObject atIndex:(NSUInteger)index {
    if (anObject == nil) return;
    
    [self mj_insertObject:anObject atIndex:index];
}

//拦击字典所有赋值、取值的事件:---- NSMutableDictionary分类
+ (void)load
{
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
         //赋值NSMutableDictionary真正基类是__NSDictionaryM
        Class cls = NSClassFromString(@"__NSDictionaryM");
        Method method1 = class_getInstanceMethod(cls, @selector(setObject:forKeyedSubscript:));
        Method method2 = class_getInstanceMethod(cls, @selector(mj_setObject:forKeyedSubscript:));
        method_exchangeImplementations(method1, method2);
        
        //获值时的真正基类是__NSDictionaryI
        Class cls2 = NSClassFromString(@"__NSDictionaryI");
        Method method3 = class_getInstanceMethod(cls2, @selector(objectForKeyedSubscript:));
        Method method4 = class_getInstanceMethod(cls2, @selector(mj_objectForKeyedSubscript:));
        method_exchangeImplementations(method3, method4);
    });
}

- (void)mj_setObject:(id)obj forKeyedSubscript:(id<NSCopying>)key {
    if (!key) return;
    
    [self mj_setObject:obj forKeyedSubscript:key];
}

- (id)mj_objectForKeyedSubscript:(id)key {
    if (!key) return nil;
    
    return [self mj_objectForKeyedSubscript:key];
}

面试题

1、讲一下OC的消息机制?

(详情见上)OC中的方法调用其实都是转成了objc_msgSend函数的调用,给receiver(方法调用者)发送了一条消息(selector方法名)
objc_msgSend底层有3大阶段
消息发送(当前类、父类中查找)、动态方法解析、消息转发

2、什么是Runtime?平时项目中有用过么?

解释:
OC是一个门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行,
OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性的函数,
平时编写的OC代码,底层都是转换成了Runtime API进行的。

具体应用:
利用关联对象(AssociatedObject)给分类添加属性
变量类的所有成员变量(修复textfield的占位文字颜色、字典转模型、自动归档和解档)
交换方法的实现(交换系统的方法,如:重写reload方法实现空白占位图自动消失和出现)
利用消息转发机制解决方法找不到的异常问题。(防Crash处理)
....

上一篇下一篇

猜你喜欢

热点阅读