Objective - C 底层IOS知识积累

RunTime

2021-06-22  本文已影响0人  叔简

数据结构

objc_object id == objc_object

objc_class

cache_t cache(成员变量),一个方法缓存的结构

① 快速查找方法执行函数
② 可增量扩展的哈希表结构
③ 局部性原理的最佳应用
局部性原理:我们将调用频次较高的几种方法,放到一块开辟的空间中,这样我们在下次调用时就能更加快速的调用方法
构成:由一个数组来实现的
数组中由多个bucket_t元素构成,bucket_t 包含:IMP指针和key

Cache_t.png

class_data_bits_t bits

class_data_bits_t主要是对class_rw_t的封装
class_rw_t代表了类相关的读写信息、对class_ro_t的封装

class_data_bits_t bits 组成

method_t

method_t.png

type Encodings技术

Type Encoding.png

数据结构总结

Runtime数据结构.png

对象、类对象、元类对象

消息传递过程

实例方法消息传递过程

1、 调用了一个实例对象A对象的实例方法
2、 通过A实例对象的isa指针找到它的类对象,类对象中遍历方法列表去查找同名的方法实现,找到后Runtime发送消息调用
3、 如果没有找到就会根据superclass指针指向的父类对象,并查找其方法列表。。。直到根类对象中的方法列表,如果找到Runtime发送消息调用,如果到根类对象还没有找到就会返回nil

类方的消息传递过程

1、 调用一个类对象的类方法。
2、 通过类对象的isa指针找到元类对象,元类对象中遍历方法列表查找同名的类方法实现,找到后直接调用
3、如果没有找到就会通过superclass指针指向父元类对象,并查找其类方法列表。。。直到根元类对象中的类方法列表,如果找到直接调用,如果没有找到进入到消息转发
4、根元类对象如果没有找到会superclass会指向根类对象,并查找同名的实例方法,如果找到直接调用,如果没有找到返回nil

消息传递

消息传递.png
消息传递Supr.png
消息传递流程.png

1、缓存查找

例: 给定值是SEL 目标值是对应bucket_t的IMP。

2、当前类中查找

对于已排序好的列表,采用二分查找算法查找方法对应执行函数
对于没有排序的列表,采用一般遍历查找方法对应执行函数

3、父类逐级查找

父类逐级查找的过程.png

消息转发

消息转发.png

Method-Swizzling

MethodSwizzling.png
Method method1 = class_getInstanceMethod(self, @selector(test));
Method method2 = class_getInstanceMethod(self, @selector(Mytest));
method_exchangeImplementations(method1, method2);

使用场景

替换系统的viewdidLoad或者viewwillDismiss中,在替换的方法中,添加我们自己的代码,例如添加时长统计,来统计进入当前界面的时长

动态添加方法

编译的时候没有这个方法,运行时有这个方法需要调用performSelector方法,在消息转发中的第一次处理函数resolveInstanceMethod动态添加DoThings:Num:方法

+ (BOOL)resolveInstanceMethod:(SEL)sel{

    NSLog(@"%@",NSStringFromSelector(sel));

    if(sel == @selector(DoThings:Num:)){
        class_addMethod([self class], sel, (IMP)MyMethodIMP, "v@:");
        return YES;
    }

    return  [super resolveInstanceMethod:sel];
}

void MyMethodIMP(id self,SEL _cmd,NSString * name,NSInteger num){
    
    NSLog(@"调用了消息转发的内容");
    NSLog(@"%@",NSStringFromSelector(_cmd));
    NSLog(@"%@",name);
    NSLog(@"%ld",(long)num);
}

动态添加调用的函数

class_addMethod([self class], sel, (IMP)MyMethodIMP, "v@:");

动态添加函数参数说明

20210622-161516.png

动态方法解析

@dynamic

-动态运行时语言将函数决议推迟到运行时。
① 当我们把一个属性标示为@dynamic的时候代表着不需要编译器在编译时,为我们生成属性的setter跟getter方法的具体实现,而在在具体运行时我们调用了setter、geter方法的时候,再去添加具体的实现。
② 这种功能也只有动态运行时特性的语言支持

MST

nsobject有父类吗?

没有nsobject是根类

当在跟类中的类方法没找到,但是有同名的实例方法实现,那么会不会崩溃?会不会产生实际的调用?

由于根元类对象的superclass指针指向了根类对象,去查找实例同名的实例方法,如果找到就会执行同名的实例方法调用

下图中打印的是什么?

笔试题1.png
[self class]会转化成     
objc_msgSend(<#id  _Nullable self#>, <#SEL  _Nonnull op, ...#>)
phone的实例对象,[self class],通过isa找到phone类对象,phone类对象是没有class的,通过superclass找到父类的。。。直到查找到根类nsobject,有calss的实现,打印出来的自然是phone

[super class]会转化成
objc_msgSendSuper(<#struct objc_super * _Nonnull super#>, <#SEL  _Nonnull op, ...#>)

objc_msgSendSuper实际的接收者仍然是phone的这个实例对象,objc_msgSendSuper传递的含义,从phone的这个实例对象的父类开始查找,  直到查找到根类nsobject有class的实现,所以打印出来的结果还是phone

打印结果都是phone

对页面的进出添加进出信息

使用MethodSwizzling来交换viewDidLoad和viewWillAppear,在替换代码中添加一些埋点操作

[obj foo]和objc_msgSend()函数之间有什么关系?

runtime如何通过Selector找到对应的IMP地址的?

能否向编译后的类中增加实例变量?

class_ro_t ro代表的是readonly
编译后的类是无法添加实例变量的。

能否向动态添加的类中增加实例变量?

可以的,动态添加的这个类过程当中只要在它调用注册方法之前去完成实例变量的添加就是可以实现的

isa指针的含义?

上一篇下一篇

猜你喜欢

热点阅读