前言

1 - OC机制很多都是基于 Runtime实现的,比如指针的弱引用。OC的消息机制属于 Runtime的一部分

2 - OC是一门动态语言,在程序运行过程中就可以修改已经编译好的代码

3 - Runtime API基本上是开源的,由 C、C++甚至汇编语言编写的

isa

1 - arm64下的 isa结构

2 - 代码示例:对 Person进行弱引用并关联对象

 1 #import <Foundation/Foundation.h>
2 #import "Person.h"
3 #import <objc/runtime.h>
4 int main(int argc, const char * argv[]) {
5
6 @autoreleasepool {
7
8 Person *person = [[Person alloc] init];
9
10 // 弱引用
11 __weak Person *weakPerson = person;
12 // 关联对象
13 objc_setAssociatedObject(person, @"name", @"rose", OBJC_ASSOCIATION_COPY_NONATOMIC);
14
15 // p/x person->isa
16 // 0x5A100100F1B
17 }
18
19 return 0;
20
21 }

转换成二进制

Class

1 - Class结构

2 - class_rw_t和 class_ro_t中都存在类名、方法列表、属性列表等。有何不同 ?

① class_rw_t里面的 methods、properties、protocols是二维数组,且可读可写。它包含了类的初始内容、分类的内容,下面以方法列表为例:方法列表1 和方法列表2 中存储的是分类方法;那么方法列表3 中存储的就是类的初始方法

② class_ro_t里面的 baseMethodList、baseProtocols、ivars、baseProperties 是一维数组,且只读。包含了类的初始内容。下面以方法列表为例

注:一个类在初始化时只有 class_ro_t,而 bits也原本指向 class_ro_t。但是随着程序员对类的使用,例如增加方法、属性、分类等操作,class_ro_t最终会被并到 class_rw_t 里面,就是说 class_rw_t原本并不存在,是后来对类不断丰富扩展才产生的

③ method_t:是对方法、函数的封装

④ Type Encoding:@encode指令可以将具体的类型表示成字符串编码

// OC中的方法参数 id和 SEL会默认隐藏
-(void)test;
// 其实质如下
-(void)test:(id)self _cmd:(SEL)_cmd; // 方法的 encode编码
// " i24 @0 :8 i16 f20 "
// i 代表返回类型 int @ 代表 id 类型 : 代表 SEL f 代表 float 类型
// 最前面的 24代表内存大小,共计占据 24个字节
// 0 代表 id参数内存地址从 0开始,占据8个字节。依次类推
-(int)test:(int)age height:(float)height;

3 - 方法缓存 cache_t

① 它用散列表来缓存曾经调用过的方法,可以提高方法的查找速度。缓存方法以散列表的结构存在

一个 bucket_t就相当于一个方法

② iOS散列表原理

在首次使用方法时,系统会事先创建一个散列表(比如长度是 10):地址编号-内容

比如调用了 personTest和 personTest2两方法。系统会先把两方法分别同掩码进行位与运算,计算出的结果便是对应散列表的地址编号,将其放入表中。注:有的编程语言是对 _mask取余操作而位与运算,其实两者得出的结果终将小于散列表长度

当我们再次调用已使用过的方法时,比如 personTest 系统会先通过位运算 @selector(personTest)  & _mask == 4取得结果 4,然后根据结果(地址编号)直接从散列表中取出方法进行调用

那么问题来了:如果有新的方法在与上掩码后得出的结果对应散列表中内存空间已经被占用,比如 @selector(personTest4) & _mask == 4已被 personTest占用,怎么办 ?苹果给出的解决办法是把结果 -1,就是说最终会把 personTest4放进 3的位置......那么问题又又又来了:如果 3的位置也已经被占用,怎么办?继续 -1往上遍历 -->表头 -->表尾 --> 计算结果 +1(5),如此就闭环遍历了整个散列表

最终当我们 -1操作直至遍历了整个列表都没有找到空闲内存可用,那么系统就会把散列表扩容(原散列表长度*2)。散列表扩容的同时会把原表中的内容全部清空,原来的方法缓存不复存在,是一份崭新的散列表

③ 方法缓存原理

    当给一个对象发送消息,首先在 objc_class中的 cache里查询

如果缓存中没有该方法,就在 class_rw_t中的 methods里查找并将结果写进 cache

当再次使用该方法,直接在 cache里面查找使用

注:当前对象调用方法,就算是父类甚至是基类的方法,也会将该方法存储进自己(当前对象)的缓存中,而不会存储进父类的缓存中。另外注意缓存方法过程中散列表扩容时的重置问题

 1 #import <Foundation/Foundation.h>
2 #import "BSClassInfo.h" // 自定义实现文件,获取缓存相关
3
4 @interface Person : NSObject
5
6 - (void)personTest;
7 @end
8
9 @implementation Person
10 - (void)personTest{
11
12 NSLog(@"%s", __func__);
13 }
14 @end
15
16 //====================================
17 @interface Student : Person
18
19 - (void)studentTest;
20 @end
21
22 @implementation Student
23 - (void)studentTest{
24
25 NSLog(@"%s", __func__);
26 }
27 @end
28
29 //====================================
30 @interface GoodStudent : Student
31
32 - (void)goodStudentTest;
33 @end
34
35 @implementation GoodStudent
36
37 - (void)goodStudentTest{
38
39 NSLog(@"%s", __func__);
40 }
41 @end
42
43 //====================================
44 int main(int argc, const char * argv[]) {
45 @autoreleasepool {
46
47 GoodStudent *goodStudent = [[GoodStudent alloc] init];
48 mj_objc_class *goodStudentClass = (__bridge mj_objc_class *)[GoodStudent class];
49
50 // 多次调用方法,验证缓存状况
51 [goodStudent goodStudentTest];
52 [goodStudent studentTest];
53 [goodStudent personTest];
54 [goodStudent goodStudentTest];
55 [goodStudent studentTest];
56
57 NSLog(@"--------------------------");
58
59 // 遍历出 goodStudentClass中的缓存方法
60 cache_t cache = goodStudentClass->cache;
61 bucket_t *buckets = cache._buckets;
62 bucket_t bucket = buckets[(long long)@selector(studentTest) & cache._mask];
63 NSLog(@"%s %p", bucket._key, bucket._imp);
64 for (int i = 0; i <= cache._mask; i++) {
65 bucket_t bucket = buckets[i];
66 NSLog(@"%s %p", bucket._key, bucket._imp);
67 }
68 }
69 return 0;
70 }

日志输出:studentTest 和 personTest都是 GoodStudent中没有的方法,但是方法却全部缓存进当前调用类 GoodStudent中

链接 - BSClassInfo.h文件

https://pan.baidu.com/s/1q3PNtAjo6YCJDnCH70Gegw

c2ws

iOS笔记 - Runtime 01:前期准备(isa结构 | Class结构 | 方法缓存)的更多相关文章

  1. 关于iOS的runtime

    runtime是一个很有意思的东西,如果你学iOS开发很经常就会用到或被问到runtime.那么runtime是什么呢,如何去了解它. runtime:中文名 运行时,系统在编译时留下的一些 类型,操 ...

  2. iOS 认识runtime 中的三个指针 isa , IMP , SEL

    runtime中函数调用经常被提及的三个概念 isa,IMP,SEL 一  isa:是类指针,之所以说isa是指针是因为Class其实是一个指向objc_class结构体的指针,而isa 是它唯一的私 ...

  3. iOS回顾笔记( 01 )

    html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,bi ...

  4. iOS回顾笔记( 01 )-- XIB和纯代码创建应用的对比

    header{font-size:1em;padding-top:1.5em;padding-bottom:1.5em} .markdown-body{overflow:hidden} .markdo ...

  5. iOS:runtime最全的知识总结

    runtime 完整总结 好东西,应该拿出来与大家分享... 南峰子博客地址:http://southpeak.github.io/blog/categories/ios/ 原文链接:http://w ...

  6. iOS开发-Runtime详解(简书)

    简介 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的.比如: [receiver message]; // ...

  7. iOS开发-Runtime详解

    iOS开发-Runtime详解 简介 Runtime 又叫运行时,是一套底层的 C 语言 API,其为 iOS 内部的核心之一,我们平时编写的 OC 代码,底层都是基于它来实现的.比如: [recei ...

  8. 【Unity Shaders】学习笔记——SurfaceShader(二)两个结构体和CG类型

    [Unity Shaders]学习笔记——SurfaceShader(二)两个结构体和CG类型 转载请注明出处:http://www.cnblogs.com/-867259206/p/5596698. ...

  9. 荼菜的iOS笔记--UIView的几个Block动画

    前言:我的第一篇文章荼菜的iOS笔记–Core Animation 核心动画算是比较详细讲了核心动画的用法,但是如你上篇看到的,有时我们只是想实现一些很小的动画,这时再用coreAnimation就会 ...

  10. Fortran学习笔记:01 基本格式与变量声明

    Fortran学习笔记目录 01 基本格式与变量声明 格式 固定格式(Fixed Format):Fortran77 程序需要满足一种特定的格式要求,具体形式参考教材 自由格式(Free Format ...

随机推荐

  1. HDMI转USB视频采集卡(ACASIS 1080P高清视频采集卡)--九五小庞

    ACASIS阿卡西斯是深圳市菲德越科技有限公司旗下数码科技品牌.菲德越是2008年成立的一家专注于采集卡.硬盘盒.集线器等专业3C配件产品,集研发.设计.生产.销售于一体的高新科技公司,我们公司以向客 ...

  2. 跳板攻击之:NPS代理转发

    跳板攻击之:NPS代理转发 目录 跳板攻击之:NPS代理转发 1 NPS介绍 2 NPS特点 3 实验环境 3.1 实验准备 3.2 实验拓扑 3.3 NPS配置 3.3.1 conf/nps.con ...

  3. lama-cleaner全部命令行参数

    usage: main.py [-h] [--host HOST] [--port PORT] [--config-installer] [--load-installer-config] [--in ...

  4. MySQL联合索引的创建规则

    1.索引应该按照最常用于查询的列的顺序创建.这样可以最大程度地提高查询性能. 2.如果查询中包含的列与索引中的列顺序不一致,则无法使用索引.因此,如果您有多个查询,每个查询都包含不同的列,那么最好为每 ...

  5. Mysql数据库基础第五章:(一)事务

    Mysql数据库基础系列 软件下载地址 提取码:7v7u 数据下载地址 提取码:e6p9 mysql数据库基础第一章:(一)数据库基本概念 mysql数据库基础第一章:(二)mysql环境搭建 mys ...

  6. Unable to preventDefault inside passive event listener invocation.

    问题:如图所示 Unable to preventDefault inside passive event listener invocation. 解决方案: 应用 CSS 属性 touch-act ...

  7. Spring随意总结

    Spring框架的优点 1.使用Spring的IOC容器,将对象之间的依赖关系交给Spring,降低组件之间的耦合性,让我们更专注于应用逻辑 2.可以提供众多服务,事务管理,WS等. 3.AOP的很好 ...

  8. npm报错最好的办法就是删掉依赖然后重装

    之前有个node工程,现在要新增antd主题,那得增加安装 craco 并修改 package.json 里的 scripts 属性 改吧改,安装一直报错: Cannot find module 'w ...

  9. disabled属性的简介和使用

    一.disabled属性的简介和使用 Html中的input元素.button元素.option元素等都具有一个disabled属性. disabled对a标签不起作用当赋予该属性时该元素将变得不可交 ...

  10. Go语言中超过1000个线程panic

    1.问题描述 2.实验 3.原理 4.解释 Close太多,Close在Windows上阻塞型的可能会新创建线程,而Linux上是非阻塞型不会新创建线程.