RunTime

作者: 代码歌 | 来源:发表于2022-04-28 09:53 被阅读0次

    类(objc_class)

    OC中的类由Class表示,其实是一个指向objc_class结构体的指针:

    typedef struct objc_class *Class;
    

    objc_class的结构:

    struct objc_class {
        Class _Nonnull isa  OBJC_ISA_AVAILABILITY;             //isa指针
    
    #if !__OBJC2__
        Class _Nullable super_class                            //父类的指针
        const char * _Nonnull name                             //类名
        long version                                            
        long info                                               
        long instance_size                                      
        struct objc_ivar_list * _Nullable ivars               //属性列表
        struct objc_method_list * _Nullable * _Nullable methodLists       //方法列表             
        struct objc_cache * _Nonnull cache                   //方法缓存    
        struct objc_protocol_list * _Nullable protocols      //协议列表
    #endif
    
    }
    

    如上所示:结构体的第一个成员变量也是isa指针,这就说明了Class本身其实也是一个对象,因此我们称之为【类对象】,类对象在编译期产生用于创建实例对象,是个单例。

    实例(objc_object)

    /// Represents an instance of a class.
    struct objc_object {
        Class isa  OBJC_ISA_AVAILABILITY;
    };
    

    实例中的isa指针指向类对象

    元类(Meta Class)

    Q:由上我们知道,类也是个对象,叫做类对象。那么类对象和类方法是从哪创建的呢?
    A:就是类中的isa指针指向的结构体,叫元类。元类中保存了创建类对象和类方法的所有信息

    • 元类是类对象的类
    • 元类的isa指针指向了自己,形成闭环
    • 由上我们知道,类本身也是个对象,所以我们可以向这个对象发送消息。(即调用类方法)
    • 任何继承自 NSObject 的元类都使用 NSObject 的元类作为自己的所处类。

    Method(objc_method)

    //方法
    struct objc_method {
        SEL method_name                                          //方法名
        char *method_types                                       //方法类型
        IMP method_imp                                           //方法实现
    }
    
    1、SEL(objc_selector)

    SEL是selector在Objective-C中的表示类型,selector其实是一个string

    • 同一个类,selector不能重复
    • 不同的类,selector可以重复
    2、IMP:指向最终实现程序的内存地址的指针

    Runtime中,Method通过selector和IMP两个属性,实现了快速查询方法及实现,相对提高了性能,又保持了灵活性。

    类缓存(objc_cache)

    iOS消息传递是怎么实现的呢?

    1. 系统首先找到消息的接收对象,然后通过对象的isa找到它的类。
    2. 在它的类中【遍历】查找method_list,是否有selector方法。
    3. 没有则查找父类的method_list。
    4. 找到对应的method,执行它的IMP。
    • 为了提高效率,系统会将调用过的方法以 @selector(xxx) 为key缓存下来。避免每次都要执行遍历操作
    • 向一个对象发送消息时,RunTime会先从缓存里找,如果有直接实现方法。如果没有遍历方法列表,找到该方法实现并缓存下来。

    Runtime应用

    关联对象(Objective-C Associated Objects):给分类增加属性
    #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
    
    方法交换(Method Swizzling)
    Method originalMethod = class_getInstanceMethod(class,originalSelector);
    Method swizzledMethod = class_getInstanceMethod(class,swizzledSelector);
    method_exchangeImplementations(originalMethod, swizzledMethod);
    

    从上面说的 Method 我们知道,Method中包含 Selector 方法名和 IMP 指向方法具体实现地址的指针,当执行 method_exchangeImplementations 方法时,将 A 方法名和 B 方法名对应的 指向具体实现的IMP 指针交换。

    KVO实现实现

    当观察 a 对象的一个属性时,系统会动态创建一个 a 当前类的子类。并将isa指针指向这个子类 B。通过重写 B 类中被观察属性的 setter 方法实现监听。具体实现如下。

    - (void)setName:(NSString *)newName { 
          [self willChangeValueForKey:@"name"];    //KVO 在调用存取方法之前总调用 
          [super setValue:newName forKey:@"name"]; //调用父类的存取方法 
          [self didChangeValueForKey:@"name"];     //KVO 在调用存取方法之后总调用
    }
    
    实现字典和模型的自动转换(MJExtension)
    #import "NSObject+RunTime.h"
    #import <objc/runtime.h>
    
    @implementation NSObject (RunTime)
    
    //步骤一:回去类的属性列表,获取属性的 KEY,加到数组里
    + (NSArray *)or_objcProperties{
        //runtime可以获取以下列表
        //Ivar:成员变量  Property:属性  Method:方法  Protocol:协议
        unsigned int count = 0;
        objc_property_t *proList = class_copyPropertyList([self class], &count);
        //创建数组
        NSMutableArray *arrayM = [NSMutableArray array];
        // 遍历所有的属性
        for (unsigned int i = 0; i < count; i++) {
            // 1. 从数组中取得属性
            /**
             C 语言的结构体指针,通常不需要 `*`
             */
            objc_property_t pty = proList[i];
            
            // 2. 从 pty 中获得属性的名称
            const char *cName = property_getName(pty);
            
            NSString *name = [NSString stringWithCString:cName encoding:NSUTF8StringEncoding];
            // 3. 属性名称添加到数组
            [arrayM addObject:name];
        }
        // 释放数组
        free(proList);
        return arrayM.copy;
    }
    
    //步骤二:遍历传入的字典,如果步骤一中的数组包含字典的 key,则对象执行 setValue for key
    + (instancetype)or_objWithDic:(NSDictionary *)dict {
        id object = [[self alloc] init];
        NSArray *proList = [self or_objcProperties];
        //遍历传入字典
        [dict enumerateKeysAndObjectsUsingBlock:^(id  _Nonnull key, id  _Nonnull obj, BOOL * _Nonnull stop) {
            //判断key是否在proList中
            if ([proList containsObject:key]) {
                [object setValue:obj forKey:key];
            }
        }];
        
        return object;
    }
    
    @end
    

    相关文章

      网友评论

          本文标题:RunTime

          本文链接:https://www.haomeiwen.com/subject/rprtyrtx.html