前言

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. 关于dynamic类型

    动态类型是从C#4才开始有的,随着DLR一起引人的. 大部分时候的行为如同object类型,对应的IL代码也是object类型. 它的特点是编译时会忽略在dynamic上进行的操作,如调用方法,读写属 ...

  2. ChatGPT API使用介绍

    1.概述 随着人工智能技术的不断发展,越来越多的AI产品被应用到各个领域,其中最具代表性的莫过于人工智能语言模型.语言模型是一种可以通过学习大量语言数据来预测文本或语音的技术,其应用范围十分广泛,如智 ...

  3. 疑问:如何替代dtb文件

    学习设备树,想添加自己定义的节点,编译好了,但不知道如何替代原来的dtb文件

  4. Ubuntu更换国内apt-get源

    更换方法: cp /etc/apt/sources.list /etc/apt/sources.list.bak sudo sed -i 's/archive.ubuntu.com/mirrors.u ...

  5. mysql8使用tmpfs内存磁盘当内存数据库的配置方法

    序: 内存关系数据库没有找到开源好用的,很多都是商用.虽然mysql有memory引擎,但写是整体锁表,没法用. 一直想将mysql放入内存中,搜索n次资料,没找到合适的,可能之前思路不对. 最近在测 ...

  6. 数据库基础day1

    数据库基础 MySQL概述 SQL 函数 概念 函数 是指一段可以直接被另一段程序调用的程序或代码. 3.1字符串函数 函数 功能 CONCAT(S1,S2,...Sn) 字符串拼接,将S1,S2,. ...

  7. Python语言课程实验报告 (第三周)

    Python语言基础实验(第三周) 一.实验目的和要求 1.了解流程控制语句的结构与类型: 2.学习语句的使用: 3.掌握流程控制语句的实际应用. 二.实验环境 软件版本:Python 3.10 64 ...

  8. 浏览器 - 重绘(repaint)重排(reflow)

    浏览器 - 重绘(repaint)重排(reflow) 网页生成过程: HTML被HTML解析器解析成DOM 树 css则被css解析器解析成CSSOM 树 结合DOM树和CSSOM树,生成一棵渲染树 ...

  9. Web Socket 长连接

    服务 package com.kinth.basic.timetask.job.donghuan.socket; import java.io.IOException; import java.net ...

  10. js把一个数组的数据平均到几个数组里面

    arr 原始数组数据 count  每个数组里面元素个数 function splitIpLit(arr,count) { let i = 0; let newArr = []; while(i &l ...