美文网首页
iOS中的Runtime

iOS中的Runtime

作者: 文小猿666 | 来源:发表于2021-03-19 14:04 被阅读0次

一.isa

isa的理解

  • 在arm64架构之前, isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
  • 从arm64架构开始,对isa进行了优化,变成了一个共用体( union )结构,还使用位域来存储更多的信息

使用64位(8个字节)存储了大量信息

源码---右方数字表示占用的位数
union isa_ t
{
    Class cls;
    uintptr_ ,t bits;
    struct (
        uintptr_ t nonpointer          : 1;
        uintptr_t has_assoc:           : 1;
        uintptr_t has_cxX_ dtor        : 1;
        uintptr_t shiftcls             : 33;
        uintptr_t magic                : 6;
        uintptr_t weakly_referenced    : 1;
        uintptr_t deallocating         : 1;
        uintptr_t has_sidetable_ rc    : 1;
        uintptr_t extra_rc             : 19;
    );
};

注意:shiftcls保存着类对象的地址值,占用33bit

位运算---设值与取值
取值用按位与& (0b0000 0001-取第八位数值)
设值用按位与&(0b1111 1110-设第八位为0) + 按位或|(0b0000 0001-设第八位为1):需要确保其他值不变,只修改想设置的值
(按位非 ~)

图片.png

二.class结构

图片.png
图片.png
图片.png

cache缓存

图片.png
Class内部结构中有个方法缓存( cache_t ) , 用散列表来缓存曾经调用过的方法,可以提高方法的查找速度(空间换时间
图片.png

SEL --函数(方法)名
imp --指向函数的指针(函数地址)

用下面一张图大概概括class的结构


2531642047642_.pic_hd.jpg

三.objc_msgSend 消息转发

OC的方法调用:消息机制,给方法调用者发送消息
objc_msgsend的执行流程可以分为3大阶段
- 消息发送

图片.png

- 动态方法解析
两个关键实现方法:+resolveInstanceMethod:/+resolveClassMethod:

图片.png

在OC中调用某个方法,当第一步,消息发送阶段未找到此方法时,会进入第二步---动态方法解析

//动态添加实例方法
- (void)other
{
    NSLog(@"%s",__ func__ );
}
+ ( BOOL)resolveInstanceMethod: (SEL )sel
{
        if(sel == @selector(test)) f
        // 获取其他方法
        Method method = class_ getInstanceMethod(self, @selector(other));
        //动态添加test方法的突現
        class_addMethod(self, sel,
            method_getImplementation(method) ,
            method_getTypeEncoding(method));
        //返回YES代表有幼恋添加方法
        return YES;
}
    return [super resolveInstanceMethod:sel];
}

- 消息转发
当方法调用的第一步,与第二步都没有调用成功的时候。会进入第三步--消息转发,执行方法 forwardingTargetForSelector

//当进入第三步时,会调用 MJCat 的test方法
- (id)forwardingTargetForSelector: (SEL)aSelector{
       if (aSelector == @selector(test)) f
          return [[MJCat alloc] init];
       }
       return [super forwardingTargetForSelector :aSelector];
}
图片.png

扩展

提醒编译器不要自动生成setter和getter的实现、不要自动生成成员变量
@dynamic age;

四.[ self class]与[super class]

一.[ self class]的底层实现

  • 1.objc_msgSend消息接收者仍然是子类对象
    所以super class与self class 本质是一样的
-(Class) class{
   return object_ getClass(self );
}
  • 2.从父类开始查找方法的实现

二.[self superclass]的底层实现
superclass方法的返回值,也是取决于消息接收(调用)者是谁

- (Class) superclass //获取调用者的父类
{
  return class_getSuperclass(object_getClass(self)); //获取self对象的父类方法
}

因此 [self superclass]与[super superclass]的本质一样

同一个类中,下面两个方法的打印结果是一样的

NSLog(@"[self class] = %@", [self class]);
NSLog(@"'[ super class]= %@"[super class]);
NSLog (@"[ self superclass] = %@", [self superclass]) ;
NSLog (@"[ super superclass] = %@", [super superclass]) ;

五.isKindOfClass和isMemberOfClass

实例方法
-(BOOL)isKindOfClass:(Class)aClass;// 判断左边实例的类对象或其父类的类对象是否跟右边类对象相等
-(BOOL)isMemberOfClass:(Class)aClass; // 判断左边实例的类对象是否跟右边类对象相等

类方法
+(BOOL)isKindOfClass:(Class)aClass;//左边类的类对象父类的类对象(即元类)是否跟右边类对象相等
+(BOOL)isMemberOfClass:(Class)aClass;//左边类的类对象(即元类)是否跟右边类对象相等

六.面试题

一.讲一下OC的消息机制

  1. OC中的方法调用其实都是转成了objc msgSend函数的调用,给receiver (方法调用者)发送了一条消息 ( selector方法名)

2.objc_ msgSend底层有3大阶段
消息发送(当前类、父类中查找)、动态方法解析、消息转发

二.什么是Runtime?平时项目中有用到过吗?
OC是一门动态性比较强的编程语言,允许很多操作推迟到程序运行时再进行
OC的动态性就是由Runtime来支撑和实现的, Runtime是一 套C语言的API ,封装了很多动态性相关的函数
使用:
1.类相关: 利用关联对象,给分类添加属性(成员变量)
2.成员变量:遍历类的所有成员变量(修改textfield的占位文字颜色(修改成员变量值)、字典转模型(YYmodel的核心)、自动归档解档)
3.方法相关
1.方法交换(hook(钩子)方法,最常用的应用是防止数组、字典等越界崩溃).
2.利用消息发送机制进行非常规的方法调用 objc_msgSend

方法交换实例:

+ (void)swizzleInstanceMethod2:(SEL)originalSel with:(SEL)swizzledSel {
    Method originalMethod = class_getInstanceMethod(self, originalSel);
    Method swizzledMethod = class_getInstanceMethod(self, swizzledSel);

    NSLog(@"给当前分类添加原类的方法originalSelector");
    BOOL didAddMethod =
    class_addMethod(self,
                    originalSel,
                    method_getImplementation(swizzledMethod),
                    method_getTypeEncoding(swizzledMethod));
    
    if (didAddMethod) {
        NSLog(@"originalSelector方法在原类中不存在,已经添加成功,用下面的方法替换其实现");
        class_replaceMethod(self,
                            swizzledSel,
                            method_getImplementation(originalMethod),
                            method_getTypeEncoding(originalMethod));
    } else {
        NSLog(@"如果原类中已存在originalSelector的话,那么添加失败而已,返回NO");
        method_exchangeImplementations(originalMethod, swizzledMethod);
    }
}

方法交换代码解析
方法交换应用场景//统计用户进入各个控制器的次数

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

//拷贝实例变量列表(最后需要调用free释放)
Ivar *Class_copyIvarList(Class cls, unsigned int *outCount)

//设置和获取成员变量的值
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)
  • Runtime中方法相关的API
//获得一个实例方法、类方法
Method class_ getInstanceMethod(Class cls, SEL name)
Method class_ _getClassMethod(Class cls, SEL name)
//方法实现相关操作
IMP class_ getMethodImplementation(Class cls, SEL name)
IMP method_ set Imp lementation (Method m, IMP imp)
void method_ exchangeImp lementat ions (Method m1, Method m2)
//拷贝方法列表(最后需要凋用free釋放)
Method *Class_ copyMethodList(Class cls, unsigned int *outCount)
//动态添加方法
B00L class_ addMethod(Class cls, SEL name, IMP imp, const char *types)
//动态替換方法
IMP class_ replaceMethod(Class cls, SEL name, IMP imp, const char *types)
//获取方法的相关信息(帯有copy的需要凋用free去釋放)
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)
char *method_copyArgumentType(Method m, unsigned int index)

相关文章

网友评论

      本文标题:iOS中的Runtime

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