runtime

作者: boy丿log | 来源:发表于2019-06-11 23:51 被阅读0次

    runtime简介

    runtime源码地址

    • 运行时(Runtime)是指将数据类型的确定由编译时推迟到了运行时
    • Runtime是一套比较底层的纯C语言API, 属于1个C语言库, 包含了很多底层的C语言API
    • 平时编写的OC代码,在程序运行过程中,其实最终会转换成Runtime的C语言代码,Runtime是Object-C的幕后工作者
    • Object-C需要Runtime来创建类和对象,进行消息发送和转发

    作用

    • 在程序运行过程中,动态的创建类,动态添加、修改这个类的属性和方法;
    • 遍历一个类中所有的成员变量、属性、以及所有方法
    • 消息传递、转发

    类和对象

    struct objc_object {
    private:
        isa_t isa;
    
    public:
    
        // ISA() assumes this is NOT a tagged pointer object
        Class ISA();
    
        // getIsa() allows this to be a tagged pointer object
        Class getIsa();
    }
    struct objc_class : objc_object {
        // Class ISA;
        Class superclass;
        cache_t cache;             // formerly cache pointer and vtable
        class_data_bits_t bits;    // class_rw_t * plus custom rr/alloc flags
    }
    

    可以看出类是继承于对象的,多了一个superClass,缓存和bits,对象是一个结构体,拥有一个isa指针。

    对象

    对象拥有一个isa,它是一个联合体(union)

    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
    };
    
    #   define ISA_BITFIELD                                                        
          uintptr_t nonpointer        : 1;  //0,代表普通指针,存储着class、metaclass对象的内存地址,1,代表优化过,使用位域存储更多的信息。        用来区分isa存的是union还是地址      
          uintptr_t has_assoc         : 1;   //是否有设置过关联对象,如果没有,释放更快                                      
          uintptr_t has_cxx_dtor      : 1;//是否有c++的析构函数                                        
          uintptr_t shiftcls          : 44; //存储着class、metaclass对象的内存地址
          uintptr_t magic             : 6;    //用来分辨对象是否未完成初始化                                     
          uintptr_t weakly_referenced : 1;   //是否被弱引用                                      
          uintptr_t deallocating      : 1;      //是否正在释放                                 
          uintptr_t has_sidetable_rc  : 1;                           //如果为1,则引用技术存储在sidetable              
          uintptr_t extra_rc          : 8//引用计数器
    #   define RC_ONE   (1ULL<<56)
    #   define RC_HALF  (1ULL<<7)
    

    struct objc_class : objc_object {
        Class superclass;
        cache_t cache;             
        class_data_bits_t bits;   
        ...
    }
    

    这里的class_data_bits_t存储了类对象的一些flag和 class_rw_t。 class_rw_t是一个结构体:

        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;
    

    其实一开始类的方法,属性,成员变量属性协议等等都是存放在class_ro_t中的,当程序运行的时候,需要将分类中的列表跟类初始的列表合并在一起的时,就会将class_ro_t中的列表和分类中的列表合并起来存放在class_rw_t中,也就是说class_rw_t中有部分列表是从class_ro_t里面拿出来的。并且最终和分类的方法合并。

    cache_t的实现可以看这个文章探寻Runtime本质(二)

    引用:
    当第一次使用方法时,消息机制通过isa找到方法之后,会对方法以SEL为keyIMP为value的方式缓存在cache的_buckets中,当第一次存储的时候,会创建具有4个空间的散列表,并将_mask的值置为散列表的长度减一,之后通过SEL & mask计算出方法存储的下标值,并将方法存储在散列表中。举个例子,如果计算出下标值为3,那么就将方法直接存储在下标为3的空间中,前面的空间会留空。
    当散列表中存储的方法占据散列表长度超过3/4的时候,散列表会进行扩容操作,将创建一个新的散列表并且空间扩容至原来空间的两倍,并重置_mask的值,最后释放旧的散列表,此时再有方法要进行缓存的话,就需要重新通过SEL & mask计算出下标值之后在按照下标进行存储了。
    如果一个类中方法很多,其中很可能会出现多个方法的SEL & mask得到的值为同一个下标值,那么会调用cache_next函数往下标值-1位去进行存储,如果下标值-1位空间中有存储方法,并且key不与要存储的key相同,那么再到前面一位进行比较,直到找到一位空间没有存储方法或者key与要存储的key相同为止,如果到下标0的话就会到下标为_mask的空间也就是最大空间处进行比较。
    当要查找方法时,并不需要遍历散列表,同样通过SEL & mask计算出下标值,直接去下标值的空间取值即可,同上,如果下标值中存储的key与要查找的key不相同,就去前面一位查找。这样虽然占用了少量控件,但是大大节省了时间,也就是说其实apple是使用空间换取了存取的时间。

    元件

    imp

    imp指的是方法的具体实现,是.m文件中的方法实现。imp相关的方法:

    • 设置
    /**
    设置block的实现。block的第一个参数为执行imp的对象,之后的参数是imp需要传入的参数
    id _block = ^ (id obj, NSInteger a,NSInteger b){
           NSLog(@"%ld %ld",a,b);
        };
    */
    OBJC_EXPORT IMP _Nonnull
    imp_implementationWithBlock(id _Nonnull block);
    
    //获取imp的block
    OBJC_EXPORT id _Nullable
    imp_getBlock(IMP _Nonnull anImp);
    
    //移除imp的block
    OBJC_EXPORT BOOL
    imp_removeBlock(IMP _Nonnull anImp);
    
    • 获取
    //这个方法不能区分类方法或对象方法
    Imp imp = class_getMethodImplementation(class,sel);
    /**
    还有一种方法是通过method获取imp,mehtod的获取可以区分类和对象,然后通过method获取imp
    */
    Mehtod method = class_getInstanceMethod(class,sel);
    Mehtod method = class_getClassMethod(class,sel);
    
    Imp imp = method_getImplementation(method)
    
    • 调用
    ((void (*) (id,SEL,NSInteger,NSInteger))imp)(self,NULL,1,2);
    
    或者
    
    int (*impyFunct)(id, SEL, int, int) = (void*) imp_implementationWithBlock(impyBlock);
    impyBlock(nil, 20, 22);
    

    值得注意的是_objc_msgForward这个imp指针,
    当获取到的imp为NULL并不代表方法未实现,只能说当前方法未重写父类方法,只有当imp为_objc_msgForward时才代表没有实现这个方法,这时会走消息转发流程。

    消息转发

    消息转发是指OC方法调用时没有获取到实现方法,而进行的一系列保护操作,一般有3个步骤。

    第一步,类方法调用,如果是实例方法,调用:

    + (BOOL)resolveInstanceMethod:(SEL)sel;
    

    如果是类方法:

    + (BOOL)resolveClassMethod:(SEL)sel;
    

    这时你可以通过sel给类动态添加方法。返回值为YES,这时runtime停止消息转发,尝试执行未找到的方法,若此时仍没有实现,则crash。为NO则继续接下来的第二步。

    + (BOOL)resolveClassMethod:(SEL)sel { NSLog(@"%s",__func__); if (sel == @selector(number)) { return class_addMethod(object_getClass(self), sel, (IMP)number, "v@:"); } return [super resolveClassMethod:sel]; }
    

    第二步,重定向,这个时候,会走

    - (id)forwardingTargetForSelector:(SEL)aSelector;
    

    允许你传一个id对象,如果返回nil,继续向下执行第三步,如果返回的对象实现了该方法,则调用,如果没实现,crash。

    - (id)forwardingTargetForSelector:(SEL)aSelector { 
      NSLog(@"消息重定向 %s",__func__);
         if (aSelector == @selector(run)) 
        { 
            return [Monkey new]; 
        } 
            return [super forwardingTargetForSelector:aSelector]; 
    }
    
    

    第三步,消息转发,会调用两个方法,

    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector;
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    

    上一个方法可以创建方法签名信息,如果返回为空或不实现会crash,下一个方法可以获取到NSIvocation对象,获取到执行对象和实现方法的所有信息。

    消息转发的作用
    • 实现多继承
    • @dynamic

    Aspects、Rac、JSPath等都是使用了这一原理。

    SEL

    SEL是对象用来查询method的“键值”。关于SEL的方法:

    //如果sel不存在则创建
    sel_registerName("sel")
    //sel不存在返回nil
    sel_getUid("sel")
    //获取sel的c字符串
    sel_getName("sel")
    //sel是否有效
    sel_isMapped("sel")
    //两个sel是否相同
    sel_isEqual(sel1,sel2)
    

    Method

    method实际上是一个结构体

    typedef struct method_t *Method;
    
    struct method_t {
        SEL name;//key值
        const char *types;//返回的类型编码
        MethodListIMP imp;//imp指针
    
        struct SortBySELAddress :
            public std::binary_function<const method_t&,
                                        const method_t&, bool>
        {
            bool operator() (const method_t& lhs,
                             const method_t& rhs)
            { return lhs.name < rhs.name; }
        };
    };
    

    关于类型编码,官方文档

    方法:

    //获取名字
    method_getName(method)
    
    //获取imp指针
    method_getImplementation(method)
    
    //获取编码
    method_getTypeEncoding(method)
    //获取参数数量
    method_getNumberOfArguments(method)
    //获取返回的类型编码,必须自己释放
    method_copyReturnType(method)
    //获取参数的类型编码,必须自己释放
    method_copyArgumentType(method)
    //获取返回的类型编码
    method_getReturnType(method)
    //获取参数的类型编码
    method_getArgumentType(method)
    //重新设置method的方法实现
    method_setImplementation(method)
    //交换两个method的实现
    method_exchangeImplementations(method1, method2)
    

    关于method_setImplementation和method_exchangeImplementations

    前一个方法主要是通过block来替换原方法实现,如Aspects和RAC都是用了这种交换方法,不需要额外的增加内存,但难度较高。
    后一个方法需要重新写一个方法,额外增加了内存开销,但相对来说使用简单,不需要了解imp具体操作流程。

    • method_setImplementation示例
      static void (*aaaImp)(__unsafe_unretained id , SEL);
        __block void (*oldImp) (__unsafe_unretained id , SEL) = NULL;
        SEL deallocSelector = sel_registerName("aaa");
        //如果之前imp不为空,证明之前已方法交换过,
        if (aaaImp == NULL) {
            //获取对象的实例方法(非类方法)
            Method deallocMethod = class_getInstanceMethod([self class], deallocSelector);
           //获得老的imp指针
            aaaImp = oldImp = (__typeof__(oldImp))method_getImplementation(deallocMethod);
          //设置新的imp指针
            method_setImplementation(deallocMethod, imp_implementationWithBlock(^(){
                if (oldImp == NULL) {//如果当前类没有实现imp,则由父类类执行方法,否则当前类执行方法。
                    struct objc_super superInfo = {
                        .receiver = self,
                        .super_class = class_getSuperclass([self class])
                    };
                    void (*msgSend)(struct objc_super *, SEL) = (__typeof__(msgSend))objc_msgSendSuper;
                    msgSend(&superInfo, deallocSelector);
                }else{
                    oldImp(self,deallocSelector);
                }
                NSLog(@"ddd");
            }));
        }
    
    • method_exchangeImplementations示例
    method_exchangeImplementations(class_getInstanceMethod([self class], @selector(aaa)), class_getInstanceMethod([self class], @selector(bbb)));
    

    Ivar

    实例变量,它是一个结构体:

    struct ivar_t {
        int32_t *offset;
        const char *name;//名字
        const char *type;//类型编码
        uint32_t alignment_raw;
        uint32_t size;
    
        uint32_t alignment() const {
            if (alignment_raw == ~(uint32_t)0) return 1U << WORD_SHIFT;
            return 1 << alignment_raw;
        }
    };
    

    方法不多:

    //获取名字
    ivar_getName(ivar)
    //获取内存偏移量
    ivar_getOffset(ivar)
    //获取内存编码
    ivar_getTypeEncoding(ivar)
    

    Protocol

    协议对象,继承于object_object.

    struct protocol_t : objc_object {
        const char *mangledName;
        struct protocol_list_t *protocols;
        method_list_t *instanceMethods;
        method_list_t *classMethods;
        method_list_t *optionalInstanceMethods;
        method_list_t *optionalClassMethods;
        property_list_t *instanceProperties;
        uint32_t size;   // sizeof(protocol_t)
        uint32_t flags;
        // Fields below this point are not always present on disk.
        const char **_extendedMethodTypes;
        const char *_demangledName;
        property_list_t *_classProperties;
    }
    

    没有成员变量,只有方法。
    方法:

    //通过字符串获取协议
    objc_getProtocol("DPCommonCollectionViewProtocol")
    //协议p2添加到p1
    protocol_addProtocol(p1, p2)
    //协议p1是否遵守p2
    protocol_conformsToProtocol(p1, p2)
    //是否相等
    protocol_isEqual(p1, p2)
    //获取名字
    protocol_getName(p1)
    
    struct objc_method_description {
     SEL _Nullable name;
      char * _Nullable types;
      };
    //获得方法描述信息
    protocol_getMethodDescription(p1,sel,required,isInstanceMehtod)
    //获得属性
    protocol_getProperty(p1,name,required,isInstanceProperty)
    

    property

    typedef struct property_t *objc_property_t;
    struct property_t {
        const char *name;
        const char *attributes;
    };
    

    方法:

    //获取属性名
    property_getName(p1)
    //获取属性对象的属性
    property_getAttributes(p1)
    
         //const char *propertyType = property_copyAttributeValue(property, "T");
        //属性名
        //const char *property_Value = property_copyAttributeValue(property, "V");
        //NSLog(@"property_name:%s \n property_attr:%s \n propertyType:%s \n property_Value:%s",property_name, property_attr, propertyType,property_Value);
        const char *property_Value = property_copyAttributeValue([self property], "W");
        NSLog(@"%s",property_Value);
    

    属性类型 name值:T value:变化
    编码类型 name值:C(copy) &(strong) W(weak) 空(assign) 等 value:无
    非/原子性 name值:空(atomic) N(Nonatomic) value:无
    变量名称 name值:V value:变化
    property_getAttributes函数返回objc_property_attribute_t结构体列表,objc_property_attribute_t结构体包含name和value。

    typedef struct {
        const char * _Nonnull name;           /**< The name of the attribute */
        const char * _Nonnull value;          /**< The value of the attribute (usually empty) */
    } objc_property_attribute_t;
    

    object

    object最重要的就是获取isa指针:

    isa

    isa包含了指向父类或元类内存地址。主要是通过object_getClass(objec),来使用的。

    object的其他方法:

    //更换isa指针类
    object_setClass(self, [DPRuntimeSimple class])
    //根据ivar获取实例id对象
    object_getIvar(obj,  ivar)
    //设置成员的布局
    object_setIvar(obj, ivar, value)
    object_setIvarWithStrongDefault( obj,  ivar, value)
    

    object_setIvar比\c object_setInstanceVariable(如果是Ivar)更快,使用内存管理

    后面我就要仔细说说object_setIvar和object_setIvarWithStrongDefault,这两个函数都和内存管理有关系。先说下它们的共同点,如果内存管理属于已知的内存管理方式(成员变量或属性属于ARC,strong或者weak),它们都没有区别。不同点就是如果是属于未知的内存管理方式,object_setIvar会把该实例变量被分配为unsafe_unretain,而object_setIvarWithStrongDefault会把该实例变量被分配为strong。
    首先我们要清楚3个概念,strong,weak和unsafe_unretain。
    strong是强引用指向并拥有那个对象,根据retainCount是否为0来确定是否释放内存
    weak是弱引用指向但并不拥有那个对象。释放空间时会自动将指针设置成nil。
    unsafe_unretain和weak类似,只是释放空间时不会将指针设置成nil,所以会有野指针的危害。
    所以,在ARC下,这两个方法的作用几乎一模一样。

    Class

    先说下class方法,后面会讲应用

    • 设置版本号
    int version = class_getVersion([self class]);
     NSLog(@"%d", version);
    class_setVersion([self class], 100);
     version = class_getVersion([self class]);
    
    • 获取实例大小
        size_t size = class_getInstanceSize([self class]);
        NSLog(@"%ld",size);
    
    • 根据名字获取实例变量
    Ivar ivar = class_getInstanceVariable([self class], "_ivar");
        //    self.aa = 100;
        NSLog(@"%s %ld %s", ivar_getName(ivar),ivar_getOffset(ivar), ivar_getTypeEncoding(ivar));
        
        NSInteger val;
        
        val = ((NSInteger (*)(id, Ivar))object_getIvar)(self, ivar);
        
        NSLog(@"%ld",val);
    

    这里需要注意的是,object_getIvar只能返回id对象,如果要返回基本数据类型的话需要强转,不然会crash。

    • class_getClassVariable
    Ivar ivar = class_getClassVariable([self class], "aaa");
     NSLog(@"%s %ld %s", ivar_getName(ivar),ivar_getOffset(ivar), ivar_getTypeEncoding(ivar));
    
    • 获取所有成员变量
    - (void)class_copyIvarList{
        unsigned int count;
        Ivar *aa = class_copyIvarList([self class], &count);
        for (int i = 0; i < count; i++) {
            Ivar ivar = aa[i];
            NSLog(@"%s %ld %s", ivar_getName(ivar),ivar_getOffset(ivar), ivar_getTypeEncoding(ivar));
        }
    }
    
    • 获取一个对象method
    Method method = class_getInstanceMethod([self class], sel_registerName("class_getInstanceMethod")
    
    • 获取一个类method
    Method method = class_getClassMethod([self class], @selector(aaa));
    
    • 获取imp
        Method imageNamedMethod = class_getClassMethod(objc_getClass("UIImage"), @selector(imageNamed:));
        Method ln_imageNamedMethod = class_getClassMethod(objc_getClass("UIImage"), @selector(ln_imageNamed:));
        IMP imageNameIMP = class_getMethodImplementation(objc_getClass("UIImage"), @selector(imageNamed:));
        IMP ln_imageNameIMP = class_getMethodImplementation(objc_getClass("UIImage"), @selector(ln_imageNamed:));
        NSLog(@"imageNameIMP = %p  %p",imageNameIMP,method_getImplementation(imageNamedMethod));
        NSLog(@"ln_imageNameIMP = %p   %p",ln_imageNameIMP, method_getImplementation(ln_imageNamedMethod));
        //    ((id (*) (id, SEL,id))imageNameIMP)(objc_getMetaClass("UIImage"),@selector(imageNamed:),@"111");
        ((id (*) (id, SEL,id))imageNameIMP)([self class],@selector(aa:),@"111");
    
    • 是否包含一个方法
    class_respondsToSelector(cls, sel)
    
    • 是否遵循协议
    class_conformsToProtocol(cls, pro)
    
    • 获取一个属性
    class_getProperty(cls, “c”)
    
    • 获取类布局类型,资料
         const uint8_t*a = class_getIvarLayout([self class]);
        NSLog(@"%s",a);
    

    如:

     interface Foo : NSObject {
     __strong id ivar0;
     __weak id ivar1;
     __weak id ivar2;
     }
     @end
    

    详情查看
    则储存 strong ivar 的 ivarLayout 的值为 0x012000

    储存 weak ivar 的 weakIvarLayout 的值为 0x1200

    1.前两位 01 表示有 0 个非 strong 对象和 1 个 strong 对象
    2.之后两位 20 表示有 2 个非 strong 对象和 0 个 strong 对象
    3.最后两位 00 为结束符,就像 cstring 的 \0 一样

    • 添加方法,如果已添加,会返回失败
    class_addMethod(clas, name, imp, "c")
    
    • 重新设置一个方法的imp
     IMP a = class_replaceMethod(clas, name, imp, "c")
    
    • 添加实例
        Class class = objc_allocateClassPair(NSObject.class, "Sark", 0);
        class_addIvar(class, "_gayFriend", sizeof(id), log2(sizeof(id)), @encode(id));
        class_addIvar(class, "_girlFriend", sizeof(id), log2(sizeof(id)), @encode(id));
        class_addIvar(class, "_company", sizeof(id), log2(sizeof(id)), @encode(id));
        class_setIvarLayout(class, (const uint8_t *)"\x01\x12"); // <--- new
        class_setWeakIvarLayout(class, (const uint8_t *)"\x11\x10"); // <--- new
        objc_registerClassPair(class);
    

    添加实例只能在创建类之后,注册类之前。

    • 动态添加属性
        objc_property_attribute_t types = { "T", "@\"NSString\"" };
        objc_property_attribute_t ownership = { "C", "" };
        objc_property_attribute_t backIvar = { "V", "_privateName" };
        objc_property_attribute_t attrs[] = { types, ownership, backIvar};
        
       
        class_addProperty([SomeClass class], "name", attrs, 3);
    
    • 动态替换属性
        NSString *propertyName = @"delegate1";
        Class targetClass = [self class];
        objc_property_attribute_t type = { "T", [[NSString stringWithFormat:@"@\"%@\"",NSStringFromClass([NSString class])] UTF8String] }; //type
        objc_property_attribute_t ownership0 = { "C", "" }; // C = copy
        objc_property_attribute_t ownership = { "N", "" }; //N = nonatomic
        objc_property_attribute_t backingivar  = { "V", [[NSString stringWithFormat:@"_%@", propertyName] UTF8String] };  //variable name
        objc_property_attribute_t attrs[] = { type, ownership0, ownership, backingivar };
        class_replaceProperty([self class], "delegate1", attrs, 3);
        NSLog(@"%@",self.delegate1);
    
    • 创建一个实例
    id a = class_createInstance([self class], 0);
    
    • 返回类起源的动态库名称
    class_getName([NSObject class])
    

    objc

    objc跟object名字有点像,但作用就大不一样了。objc方法是负责runtime的全局方法。

    • 操作类
    //获取类
    objc_getClass("UITableViewController")
    //获取元类
    objc_getMetaClass("UITableViewController");
    //objc_getClass与这个函数的不同之处在于,如果这个类不是已注册,\c objc_getClass调用类处理程序回调,然后检查第二个的时候查看这个类是否注册了。此函数不调用类处理程序回调
    objc_lookUpClass("");
    //与objc_getClass相同但是如果没有类的时候会终止进程
    objc_getRequiredClass("UITableViewController");
    //获取类的数量
    int newNumClasses =  objc_getClassList(NULL, 0)
    //获取所有类
    Class *classes = (Class *)malloc(sizeof(Class) * (newNumClasses + 1)); // 2
        newNumClasses = objc_getClassList(classes, newNumClasses);
    //获取所有类,outcount为类数量
    unsigned int outCount = 0;
    Class *classLiset =objc_copyClassList(&outCount);
    
    //创建类和元类
    Class class = objc_allocateClassPair([NSObject class], "AA", 0);
    //注册,未注册的不能通过NSClassFromString获取class
    objc_registerClassPair(class);
    //objc_disposeClassPair只能销毁由objc_allocateClassPair创建的类,当有实例存在或者它的子类存在时,调用这个函数会抛出异常。 objc_disposeClassPair(class);
    
    • 协议
    //获取协议
    Protocol*protocol = objc_getProtocol("UITableViewDelegate");
    //获取所有协议
    Protocol * __unsafe_unretained *list =  objc_copyProtocolList(&count);
        for (int i = 0; i < count; i++) {
            NSLog(@"%s",protocol_getName(list[i]));
        }
    //创建注册协议
    Protocol*a = objc_allocateProtocol("aaaaaaa");
    objc_registerProtocol(a);
    
    
    • 动态库
    //获取所有动态库,及获取动态库名字
    const char **list = objc_copyImageNames(&count);
        for (int i = 0; i < count; i++) {
            unsigned int count1 = 0;
            const char **images = objc_copyClassNamesForImage(list[i], &count1);
            for (int j = 0; j < count1; j++) {
                NSLog(@"className:::::%s",images[j]);
            }
            free(images);
        }
        free(list);
    
    
    • 消息
    /**
     消息转发,用来实现_objc_msgForward
     */
    - (void)objc_setForwardHandler{
        //    objc_setForwardHandler(fwd, fwd)
    }
    /**
     主动报错, Terminating app due to uncaught exception 'NSGenericException', reason: '*** Collection <DPObjcSimple: 0x6000017dfcc0> was mutated while being enumerated.
     */
    - (void)objc_enumerationMutation{
        objc_enumerationMutation(self);
    }
    void (aaa)(){
        NSLog(@"失败了");
    };
    
    /**
     捕获异常
     */
    - (void)objc_setEnumerationMutationHandler{
        
        objc_setEnumerationMutationHandler(aaa);
        objc_enumerationMutation(self);
    }
    
    • weak指针
      该函数加载一个弱指针引用的对象,并在对其做retain和autoreleasing操作后返回它。这样,对象就可以在调用者使用它时保持足够长的生命周期。该函数典型的用法是在任何有使用__weak变量的表达式中使用。
    
        //    objc_loadWeak(self);
    //    该函数的典型用法是用于__weak变量做为赋值对象时。
        //    objc_storeWeak(self, self);
    
    • 动态关联
    objc_setAssociatedObject(obj, key, value, policy)
    objc_getAssociatedObject(objc,key)
    objc_removeAssociatedObjects(obj)
    

    动态关联不支持KVO,KVO的支持与否与包不包含这个对象无关,KVO重写了对象的set和get方法。

    消息发送流程

    objc_msgSend

    objc_msgSend发送消息与imp类似:

     ((void (*) (id,SEL,NSInteger,NSInteger))imp)(self,NULL,1,2);
    
     ((void(*)(id,SEL))objc_msgSend)(self,@selector(log));
    

    不一样的是objc_msgSend的第二个参数为selector。

    objc_msgSendSuper

      struct objc_super superInfo = {
            .receiver = self,
            .super_class = class_getSuperclass(object_getClass(self))
        };
        ((void(*)(struct objc_super *,SEL))objc_msgSendSuper)(&superInfo,@selector(log));
    

    向父类发送消息

    method_invoke

    ((void (*)(id, Method))method_invoke)(self,class_getInstanceMethod([self class], @selector(log)));
    

    根据method调用方法。

    _objc_msgForward

    消息转发。

    super与self的区别

    当调用super的时候,实际上调用的是objc_msgSendSuper方法,这个方法的第一个参数是一个结构体

    struct objc_super superInfo = {
            .receiver = self,
            .super_class = class_getSuperclass(object_getClass(self))
        };
    

    它有两个值,一个是receiver,它指的是消息处理的对象,第二个是superclass,指的是调用的父类。
    即调用super的时候,调用者依然是self,但是调用的方法确是从父类中查询的。
    self方法调用的是objc_msgSend。

    class和object_getClass

    class是类实现的方法,一般是返回本类,如[super class]、[[super class] class]返回的都是当前类的对象。
    而object_getClass获取的则是isa指针。这里要说一下类和元类。我们可以发现,类的创建方法名为objc_allocateClassPair,一对......这里的一对指的是在创建类的时候自动创建了元类,类用来存储属性和方法,而类方法(+)和属性则由元类来存储。这里需要了解下:


    class和isa的关系

    需要注意的是,NSObjec的元类的父类为NSObject类对象,isa指针指向自己。NSObject类的父类为nil。

    这里有道面试题,来考下你:

    @implementation NSObject (n)
    - (void)foo {
        NSLog(@"IMP: -[NSObject (Sark) foo]");
    }
    @end
    

    定义这个方法后,调用方法

    [NSObject foo];
    

    会crash吗?

    接下来说一些runtime的具体应用场景

    监听方法的参数,以及改变执行的顺序。

    将需要监听的方法的imp设为_objc_msgForward,然后走消息转发最后一步。方法交换对象的forwardInvocation:方法,自己拿到invocation后进行解析和调用。具体可以看Aspects和rac。

    自己实现KVO

    void *Shine_KVO = &Shine_KVO;
    static dispatch_once_t predicate;
    @implementation DPRuntimeKVO
    //根据keypath 获取set方法
    -(SEL)getNewSelector:(NSString *)selectorName
    {
        NSString *firstChar = [selectorName substringToIndex:1];
        NSString *upFirst = [firstChar uppercaseString];
        NSString *otherChar = [selectorName substringFromIndex:1];
        NSString *newSelectorName = [NSString stringWithFormat:@"set%@%@:",upFirst,otherChar];
        return  NSSelectorFromString(newSelectorName);
    }
    //根据set方法 获取keypath
    -(NSString *)getKeypath:(SEL)selector
    {
        NSString *selectorName = NSStringFromSelector(selector);
        selectorName = [selectorName substringFromIndex:3];
        NSString *firstChar = [selectorName substringToIndex:1];
        NSString *lowFirst = [firstChar lowercaseString];
        NSString *otherChar = [selectorName substringFromIndex:1];
        
        NSString *newChar = [NSString stringWithFormat:@"%@%@",lowFirst,otherChar];
        return  [newChar substringToIndex:newChar.length-1];
    }
    - (void)shine_observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary<NSKeyValueChangeKey,id> *)change{
        NSLog(@"%@",[object valueForKey:keyPath]);
    }
    void setObj(id sf,SEL cd,id value){
        NSString *keypath = [sf getKeypath:cd];
        id oldValue = [sf valueForKey:keypath];
        NSMutableDictionary *change = @{}.mutableCopy;
        if (oldValue != nil) {
            change[@"old"] = oldValue;
        }
        if (value != nil) {
            change[@"new"] = value;
        }
        //取出当前类
        id class = [sf class];
        
        NSLog(@"%@   %@", class, class_getSuperclass(class));
        //指向父类
        object_setClass(sf, class_getSuperclass(class));
        //向父类发送消息
        ((void (*)(id, SEL, id))objc_msgSend)(sf, cd ,value);
        //    //获取动态绑定对象
        id observe = objc_getAssociatedObject(sf, &Shine_KVO);
        //    //监听回调
        ((void(*)(id,SEL,id,id,id))objc_msgSend)(observe, @selector(shine_observeValueForKeyPath:ofObject:change:),keypath,sf,change);
        //修改指向
        object_setClass(sf, class);
    }
    
    
    -(void)shine_addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath
    {
        // 1.动态生成一个派生类
        static Class newClass;
        
        dispatch_once(&predicate, ^{
            NSString * oldClassName = NSStringFromClass([self class]);
            NSString *newClassName = [NSString stringWithFormat:@"Shine_%@",oldClassName];
            //创建派生类
            newClass = objc_allocateClassPair([self class], [newClassName UTF8String], 0);
            //注册派生类
            objc_registerClassPair(newClass);
            //修改被观察者的isa指针,指向自定义的类
            object_setClass(self, newClass);
            //动态绑定
            objc_setAssociatedObject(self, &Shine_KVO, observer, OBJC_ASSOCIATION_ASSIGN);
        });
        //2.生成新的set方法
        SEL selector = [self getNewSelector:keyPath];
        //3.添加新的set方法
        class_addMethod(newClass, selector, (IMP)setObj, "v@:@");
        
    }
    - (void)dealloc
    {
        predicate = 0;
        objc_disposeClassPair([self class]);
    }
    

    动态创建类并添加到ARC

    参考,sunny的博客。详情查看

    static void fixup_class_arc(Class class) {
        struct {
            Class isa;
            Class superclass;
            struct {
                void *_buckets;
    #if __LP64__
                uint32_t _mask;
                uint32_t _occupied;
    #else
                uint16_t _mask;
                uint16_t _occupied;
    #endif
            } cache;
            uintptr_t bits;
        } *objcClass = (__bridge typeof(objcClass))class;
    #if !__LP64__
    #define FAST_DATA_MASK 0xfffffffcUL
    #else
    #define FAST_DATA_MASK 0x00007ffffffffff8UL
    #endif
        struct {
            uint32_t flags;
            uint32_t version;
            struct {
                uint32_t flags;
            } *ro;
        } *objcRWClass = (typeof(objcRWClass))(objcClass->bits & FAST_DATA_MASK);
    #define RO_IS_ARR 1<<7
        objcRWClass->ro->flags |= RO_IS_ARR;
    }
    

    调用

    Class class = objc_allocateClassPair(NSObject.class, "Sark", 0);
        class_addIvar(class, "_gayFriend", sizeof(id), log2(sizeof(id)), @encode(id));
        class_addIvar(class, "_girlFriend", sizeof(id), log2(sizeof(id)), @encode(id));
        class_addIvar(class, "_company", sizeof(id), log2(sizeof(id)), @encode(id));
        class_setIvarLayout(class, (const uint8_t *)"\x01\x12"); // <--- new
        class_setWeakIvarLayout(class, (const uint8_t *)"\x11\x10"); // <--- new
        objc_registerClassPair(class);
        fixup_class_arc(class);
    

    字典转模型

    参考YYModel。

    关联weak对象

    runtime动态关联属性有

    typedef OBJC_ENUM(uintptr_t, objc_AssociationPolicy) {
        OBJC_ASSOCIATION_ASSIGN = 0,     
        OBJC_ASSOCIATION_RETAIN_NONATOMIC = 1, /
        OBJC_ASSOCIATION_COPY_NONATOMIC = 3,  
        OBJC_ASSOCIATION_RETAIN = 01401,  
        OBJC_ASSOCIATION_COPY = 01403          
    };
    

    并没有weak选项,我们关联assign类型,但是属性销毁而指针没有销毁的时候会造成野指针,那么如何设置一个weak对象呢?
    方案一、在关联的对象销毁时将引用的对象指针置nil。为了不重写对象的dealloc方法,你可以写个NSObject分类,
    就像DPRouter里面的,

    - (void)dp_addDellocTask:(dp_deallocTask)task;
    

    在设置时,调用引用对象的这个方法。
    方案二、自己新建一个类,拥有一个weak对象,用strong类型设置一个这个类的关联,然后weak属性设置成需要引用的值。
    就像这样

    static void *kdpActionBlock = & kdpActionBlock;
    static void *kDPCategoryActionViewController = &kDPCategoryActionViewController;
    
    @interface UIAlertActionWithController : NSObject
    @property (nonatomic, weak) UIAlertController * alertViewController;
    @end
    @implementation UIAlertActionWithController
    
    
    @end
    
    @implementation UIAlertAction (DPCategory)
    
    - (void)setAlertViewController:(UIAlertActionWithController *)model{
        objc_setAssociatedObject(self, kDPCategoryActionViewController, model, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    
    - (UIAlertActionWithController *)alertViewActionWithController{
        return objc_getAssociatedObject(self, kDPCategoryActionViewController);
    }
    
    - (UIAlertController *)alertViewController{
        return [self alertViewActionWithController].alertViewController;
    }
    
    

    github项目的UIAlertController分类。

    总结

    runtime能够帮助我们在开发过程中更灵活的掌控代码。

    相关文章

      网友评论

          本文标题:runtime

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