美文网首页
Objective-C:Runtime

Objective-C:Runtime

作者: zhouluyao | 来源:发表于2018-07-30 18:54 被阅读20次

    objc_msgSend底层调用有3大阶段

    1.消息发送

    objc_msgSend执行流程01-消息发送.png

    2.动态方法解析

    objc_msgSend执行流程02-动态方法解析.png
    void c_other(id self, SEL _cmd)
    {
        NSLog(@"c_other - %@ - %@", self, NSStringFromSelector(_cmd));
    }
    
    
    - (void)other
    {
        NSLog(@"%s", __func__);
    }
    
    + (BOOL)resolveInstanceMethod:(SEL)sel
    {
        if (sel == @selector(test)) {
            // 获取其他方法
            Method method = class_getInstanceMethod(self, @selector(other));
    
            // 动态添加test方法的实现
            class_addMethod(self, sel,
                            method_getImplementation(method),
                            method_getTypeEncoding(method));
    
    // 第一个参数是object_getClass(self)
    //        class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
    
            // 返回YES代表有动态添加方法
            return YES;
        }
        return [super resolveInstanceMethod:sel];
    }
    

    3.消息转发

    objc_msgSend的执行流程03-消息转发.png
    int __forwarding__(void *frameStackPointer, int isStret) {
        id receiver = *(id *)frameStackPointer;
        SEL sel = *(SEL *)(frameStackPointer + 8);
        const char *selName = sel_getName(sel);
        Class receiverClass = object_getClass(receiver);
    
      // 调用 forwardingTargetForSelector:
      if (class_respondsToSelector(receiverClass, @selector(forwardingTargetForSelector:))) 
      {
          id forwardingTarget = [receiver forwardingTargetForSelector:sel];
          if (forwardingTarget && forwardingTarget != receiver) 
          {
              return objc_msgSend(forwardingTarget, sel, ...);
          }
      }
    
        // 调用 methodSignatureForSelector 获取方法签名后再调用 forwardInvocation
        if (class_respondsToSelector(receiverClass, @selector(methodSignatureForSelector:)))
        {
            NSMethodSignature *methodSignature = [receiver methodSignatureForSelector:sel];
            if (methodSignature && class_respondsToSelector(receiverClass, @selector(forwardInvocation:))) 
            {
                NSInvocation *invocation = [NSInvocation _invocationWithMethodSignature:methodSignature frame:frameStackPointer];
    
                [receiver forwardInvocation:invocation];
    
                void *returnValue = NULL;
                [invocation getReturnValue:&value];
                return returnValue;
            }
        }
    
        if (class_respondsToSelector(receiverClass,@selector(doesNotRecognizeSelector:)))
        {
            [receiver doesNotRecognizeSelector:sel];
        }
    
        // The point of no return.
        kill(getpid(), 9);
    }
    

    一个具体的使用

    /************************消息转发**************************/
    
    - (id)forwardingTargetForSelector:(SEL)aSelector
    {
        if (aSelector == @selector(test)) {
            // objc_msgSend([[MJCat alloc] init], aSelector)
            return [[MJCat alloc] init]; //返回处理方法的调用对象
        }
        return [super forwardingTargetForSelector:aSelector];
    }
    
    /**************************上面代码没有实现时调用以下代码************************/
    
    // 方法签名:返回值类型、参数类型
    - (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
    {
        if (aSelector == @selector(test)) {
            return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
    //        return [NSMethodSignature signatureWithObjCTypes:"v@:i"]; 字节数可以省略
        }
        return [super methodSignatureForSelector:aSelector];
    }
    
    // NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
    - (void)forwardInvocation:(NSInvocation *)anInvocation
    {
        [anInvocation invokeWithTarget:[[MJCat alloc] init]];
    }
    

    isa_t结构体

        struct {
            uintptr_t nonpointer        : 1;//0,代表普通的指针,存储着Class、Meta-Class对象的内存地址 1,代表优化过,使用位域存储更多的信息
            uintptr_t has_assoc         : 1;//是否有设置过关联对象,如果没有,释放时会更快
            uintptr_t has_cxx_dtor      : 1;//是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
            uintptr_t shiftcls          : 33; //存储着Class、Meta-Class对象的内存地址信息
            uintptr_t magic             : 6;//用于在调试时分辨对象是否未完成初始化
            uintptr_t weakly_referenced : 1;//是否有被弱引用指向过,如果没有,释放时会更快
            uintptr_t deallocating      : 1;//对象是否正在释放
            uintptr_t has_sidetable_rc  : 1;//引用计数器是否过大无法存储在isa中,如果为1,那么引用计数会存储在一个叫SideTable的类的属性中
            uintptr_t extra_rc          : 19;//里面存储的值是引用计数器减1
        };
    
    Class结构.png
    class_rw_t里面的methods、properties、protocols是二维数组,是可读可写的,包含了类的初始内容、分类的内容
    class_ro_t里面的baseMethodList、baseProtocols、ivars、baseProperties是一维数组,是只读的,包含了类的初始内容

    method包含:SEL name,types,方法的实现

    method_t.png

    types包含了函数返回值、参数编码的字符串

    IMP代表函数的具体实现

    SEL代表方法/函数名,一般叫做选择器,底层结构跟char *类似

    1.可以通过@selector()和sel_registerName()获得

    2.可以通过sel_getName()和NSStringFromSelector()转成字符串

    3.不同类中相同名字的方法,所对应的方法选择器是相同的

    Class内部结构中有个方法缓存(cache_t),用散列表(哈希表)来缓存曾经调用过的方法,可以提高方法的查找速度

    hash表类似字典,一个key对应一个value,只不过hash表里面的数据根据key生成索引,能够快速的取出数据


    方法缓存.png

    面试题及具体应用:

    讲一下 OC 的消息机制

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

    什么是Runtime?

    1.OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
    2.OC的动态性就是由Runtime来支撑和实现的,Runtime是一套C语言的API,封装了很多动态性相关的函数,源码由C\C++\汇编语言编写
    3.平时编写的OC代码,底层都是转换成了Runtime API进行调用

    平时在项目中的应用

    1.利用关联对象(AssociatedObject)给分类添加属性

    
    @implementation NSObject (Property)
    
    - (NSString *)name
    {
        // 根据关联的key,获取关联的值。
        return objc_getAssociatedObject(self, key);
    }
    
    - (void)setName:(NSString *)name
    {
        // 第一个参数:给哪个对象添加关联
        // 第二个参数:关联的key,通过这个key获取
        // 第三个参数:关联的value
        // 第四个参数:关联的策略
        objc_setAssociatedObject(self, key, name, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
    }
    

    2.遍历类的所有成员变量(修改textfield的占位文字颜色、字典转模型、自动归档解档)

    2.1 、遍历UITextField的私有属性,修改placeholder的颜色
        unsigned int count;
        Ivar *ivars = class_copyIvarList([UITextField class], &count);
        for (int i = 0; i < count; i++) 
        {
            // 取出i位置的成员变量
            Ivar ivar = ivars[i];
            NSLog(@"%s %s", ivar_getName(ivar), ivar_getTypeEncoding(ivar));
        }
        free(ivars);
        
        self.textField.placeholder = @"请输入用户名";
        
        [self.textField setValue:[UIColor redColor]forKeyPath:@"_placeholderLabel.textColor"];
        
        UILabel *placeholderLabel = [self.textField valueForKeyPath:@"_placeholderLabel"];
        placeholderLabel.textColor = [UIColor redColor];
        
        NSMutableDictionary *attrs = [NSMutableDictionary dictionary];
        attrs[NSForegroundColorAttributeName] = [UIColor redColor];
        self.textField.attributedPlaceholder = [[NSMutableAttributedString alloc] initWithString:@"请输入用户名" attributes:attrs];
    
    2.2、字典转模型
    + (instancetype)ly_objectWithJson:(NSDictionary *)json
    {
        id obj = [[self alloc] init];
        
        unsigned int count;
        Ivar *ivars = class_copyIvarList(self, &count); //利用Runtime遍历所有的成员变量
        for (int i = 0; i < count; i++) {
            // 取出i位置的成员变量
            Ivar ivar = ivars[i];
            NSMutableString *name = [NSMutableString stringWithUTF8String:ivar_getName(ivar)];
            [name deleteCharactersInRange:NSMakeRange(0, 1)]; //去掉变量名前面的_
            
            // 设值
            id value = json[name];
            if ([name isEqualToString:@"ID"]) {
                value = json[@"id"];
            }
            [obj setValue:value forKey:name]; //利用KVC设值
        }
        free(ivars);
        return obj;
    }
    
    3.交换方法实现
          MJPerson *person = [[MJPerson alloc] init];
          Method runMethod = class_getInstanceMethod([MJPerson class], @selector(run));
          Method testMethod = class_getInstanceMethod([MJPerson class], @selector(test));
          method_exchangeImplementations(runMethod, testMethod);
          [person run];  //-[MJPerson test]
    
        //替换方法的实现
        class_replaceMethod([MJPerson class], @selector(run), imp_implementationWithBlock(^{
            NSLog(@"123123");
        }), "v");
    
    + (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);
    }
    
    类簇,交换系统类的方法实现,NSString、NSArray、NSDictionary,真实类型是其他类型
    + (void)load
    {
        static dispatch_once_t onceToken;
        dispatch_once(&onceToken, ^{
            // 类簇:NSString、NSArray、NSDictionary,真实类型是其他类型
            Class cls = NSClassFromString(@"__NSArrayM");
           //Class cls = NSClassFromString(@"__NSDictionaryM");  可变字典
           // Class cls2 = NSClassFromString(@"__NSDictionaryI"); 不可变字典
            Method method1 = class_getInstanceMethod(cls, @selector(insertObject:atIndex:));
            Method method2 = class_getInstanceMethod(cls, @selector(mj_insertObject:atIndex:));
            method_exchangeImplementations(method1, method2);
        });
    }
    
    4.利用消息转发机制解决方法找不到的异常问题

    objc_msgSend消息发送,动态方法解析,消息转发,添加方法实现防止触发找不到方法的异常问题

    5.创建一个类
        // 创建类
        Class newClass = objc_allocateClassPair([NSObject class], "MJDog", 0);
        class_addIvar(newClass, "_age", 4, 1, @encode(int));
        class_addIvar(newClass, "_weight", 4, 1, @encode(int));
        class_addMethod(newClass, @selector(run), (IMP)run, "v@:");
        // 注册类
        objc_registerClassPair(newClass);
        
        MJPerson *person = [[MJPerson alloc] init];
        object_setClass(person, newClass);
        [person run];
    
        id dog = [[newClass alloc] init];
        [dog setValue:@10 forKey:@"_age"];
        [dog setValue:@20 forKey:@"_weight"];
        [dog run];
    
        NSLog(@"%@ %@", [dog valueForKey:@"_age"], [dog valueForKey:@"_weight"]);
        
        // 在不需要这个类时释放
    //    objc_disposeClassPair(newClass);
    
      // 获取成员变量信息
        Ivar ageIvar = class_getInstanceVariable([MJPerson class], "_age");
        NSLog(@"%s %s", ivar_getName(ageIvar), ivar_getTypeEncoding(ageIvar));//_age i
    
      // 设置和获取成员变量的值
        Ivar nameIvar = class_getInstanceVariable([MJPerson class], "_name");
    
        MJPerson *person = [[MJPerson alloc] init];
        object_setIvar(person, nameIvar, @"123");
        object_setIvar(person, ageIvar, (__bridge id)(void *)10);
        NSLog(@"%@ %d", person.name, person.age); //123 10
    
    给对象设置对应的Class
        MJPerson *person = [[MJPerson alloc] init];
        [person run]; //[MJPerson run]
        
        object_setClass(person, [MJCar class]);
        [person run]; //[MJCar run]
    

    Runtime API

    1.类

    Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)   //动态创建一个类(参数:父类,类名,额外的内存空间)
    
    void objc_registerClassPair(Class cls) //注册一个类(要在类注册之前添加成员变量)
    
    void objc_disposeClassPair(Class cls)//销毁一个类
    
    Class object_getClass(id obj) //获取isa指向的Class
    
    Class object_setClass(id obj, Class cls)//设置isa指向的Class
    
    BOOL object_isClass(id obj)//判断一个OC对象是否为Class
    
    BOOL class_isMetaClass(Class cls) //判断一个Class是否为元类
    
    Class class_getSuperclass(Class cls)//获取父类
    

    2.成员变量

    Ivar class_getInstanceVariable(Class cls, const char *name)//获取一个实例变量信息
    
    Ivar *class_copyIvarList(Class cls, unsigned int *outCount) //拷贝实例变量列表(最后需要调用free释放)
    
    void object_setIvar(id obj, Ivar ivar, id value)//设置成员变量的值
    
    id object_getIvar(id obj, Ivar ivar)//获取成员变量的值
    
    BOOL class_addIvar(Class cls, const char * name, size_t size, uint8_t alignment, const char * types)//动态添加成员变量(已经注册的类是不能动态添加成员变量的)
      
    const char *ivar_getName(Ivar v)//获取成员变量的相关信息
    
    const char *ivar_getTypeEncoding(Ivar v)//获取成员变量的相关信息
    

    3.属性

    objc_property_t class_getProperty(Class cls, const char *name)//获取一个属性
    
    objc_property_t *class_copyPropertyList(Class cls, unsigned int *outCount)//拷贝属性列表(最后需要调用free释放)
    
    BOOL class_addProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,unsigned int attributeCount)  //动态添加属性
    
    void class_replaceProperty(Class cls, const char *name, const objc_property_attribute_t *attributes,unsigned int attributeCount)  //动态替换属性
    
    const char *property_getName(objc_property_t property)//获取属性的一些信息
    
    const char *property_getAttributes(objc_property_t property)//获取属性的一些信息
    

    4.方法

    Method class_getInstanceMethod(Class cls, SEL name)//获得一个实例方法
    
    Method class_getClassMethod(Class cls, SEL name)//  获得一个类方法
    
    IMP class_getMethodImplementation(Class cls, SEL name) //获取方法实现
    
    IMP method_setImplementation(Method m, IMP imp) //设置方法实现
    
    void method_exchangeImplementations(Method m1, Method m2) //交换方法实现
    
    Method *class_copyMethodList(Class cls, unsigned int *outCount)//拷贝方法列表(最后需要调用free释放)
    
    BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)//动态添加方法
    
    IMP class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)//动态替换方法
    
    SEL method_getName(Method m) //获取方法的相关信息
    
    IMP method_getImplementation(Method m) //获取方法的相关信息
    
    const char *method_getTypeEncoding(Method m) //获取方法的相关信息
    
    unsigned int method_getNumberOfArguments(Method m) //获取方法的相关信息
    
    char *method_copyReturnType(Method m) //获取方法的相关信息(带有copy的需要调用free去释放)
    
    char *method_copyArgumentType(Method m, unsigned int index) //获取方法的相关信息(带有copy的需要调用free去释放)
    
    const char *sel_getName(SEL sel) //选择器相关
    
    SEL sel_registerName(const char *str)//选择器相关
    
    IMP imp_implementationWithBlock(id block)//用block作为方法实现
    
    id imp_getBlock(IMP anImp)//用block作为方法实现
    
    BOOL imp_removeBlock(IMP anImp)//用block作为方法实现
    
    
    

    bucket_t、cache_t、method_t、class_ro_t、class_rw_t数据结构

    struct bucket_t {
        cache_key_t _key;
        IMP _imp;
    };
    
    struct cache_t {
        bucket_t *_buckets;
        mask_t _mask;
        mask_t _occupied;
        
        IMP imp(SEL selector)
        {
            mask_t begin = _mask & (long long)selector;
            mask_t i = begin;
            do {
                if (_buckets[i]._key == 0  ||  _buckets[i]._key == (long long)selector) {
                    return _buckets[i]._imp;
                }
            } while ((i = cache_next(i, _mask)) != begin);
            return NULL;
        }
    };
    
    struct method_t {
        SEL name;
        const char *types;
        IMP imp;
    };
    
    struct class_ro_t {
        uint32_t flags;
        uint32_t instanceStart;
        uint32_t instanceSize;  // instance对象占用的内存空间
    #ifdef __LP64__
        uint32_t reserved;
    #endif
        const uint8_t * ivarLayout;
        const char * name;  // 类名
        method_list_t * baseMethodList;
        protocol_list_t * baseProtocols;
        const ivar_list_t * ivars;  // 成员变量列表
        const uint8_t * weakIvarLayout;
        property_list_t *baseProperties;
    };
    
    struct class_rw_t {
        uint32_t flags;
        uint32_t version;
        const class_ro_t *ro;
        method_list_t * methods;    // 方法列表
        property_list_t *properties;    // 属性列表
        const protocol_list_t * protocols;  // 协议列表
        Class firstSubclass;
        Class nextSiblingClass;
        char *demangledName;
    };
    
    /* OC对象 */
    struct objc_object {
        void *isa;
    };
    
    /* 类对象 */
    struct objc_class : objc_object {
        Class superclass;
        cache_t cache;
        class_data_bits_t bits;
    public:
        class_rw_t* data() {
            return bits.data();
        }
        
        objc_class* metaClass() {
            return (objc_class *)((long long)isa & ISA_MASK);
        }
    };
    
    /* OC对象 */
    struct ly_objc_object {
        void *isa;
    };
    
    /* 类对象 */
    struct ly_objc_class : ly_objc_object {
        Class superclass;
        cache_t cache;
        class_data_bits_t bits;
    public:
        class_rw_t* data() {
            return bits.data();
        }
        
        ly_objc_class* metaClass() {
            return (ly_objc_class *)((long long)isa & ISA_MASK);
        }
    };
    
    //由于方法缓存是存在hash表中,存入的下标为方法名&cache._mask
      bucket_t *buckets = cache._buckets;
      bucket_t bucket = buckets[(long long)@selector(studentTest) & cache._mask];
      NSLog(@"%s %p", bucket._key, bucket._imp);
    //遍历方法缓存中方法的key和实现        
      for (int i = 0; i <= cache._mask; i++) {
          bucket_t bucket = buckets[i];
          NSLog(@"%s %p", bucket._key, bucket._imp);
      }
    
    

    [super message]的底层实现

    1.消息接收者仍然是子类对象

    2.从父类开始查找方法的实现

    OBJC_EXPORT void objc_msgSendSuper(void /* struct objc_super *super, SEL op, ... */ )
    
    
    /// Specifies the superclass of an instance. 
    struct objc_super {
        /// Specifies an instance of a class.
        __unsafe_unretained id receiver;
    
        /// Specifies the particular superclass of the instance to message. 
    #if !defined(__cplusplus)  &&  !__OBJC2__
        /* For compatibility with old objc-runtime.h header */
        __unsafe_unretained Class class;
    #else
        __unsafe_unretained Class super_class;
    #endif
        /* super_class is the first class to search */
    };
    
    
    struct objc_super {
        __unsafe_unretained _Nonnull id receiver; // 消息接收者
        __unsafe_unretained _Nonnull Class super_class; // 消息接收者的父类
    };
    
        // super调用的receiver仍然是MJStudent对象
        [super run];
    
        NSLog(@"[super class] = %@", [super class]); // MJStudent
        NSLog(@"[super superclass] = %@", [super superclass]); // MJPerson
    
    
    - (void)print
    {
        [super print];
        
        objc_msgSendSuper(
        {
            self,
            [MJPerson class]
        }, sel_registerName("print"));
    }
    
    
    

    objc_msgSendSuper的工作原理应该是这样的:
    从objc_super结构体指向的superClass父类的方法列表开始查找selector,找到后以objc->receiver去调用父类的这个selector。注意,最后的调用者是objc->receiver,而不是super_class!

    相关文章

      网友评论

          本文标题:Objective-C:Runtime

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