前言

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. 流量加密之:使用Openssl反弹加密shell

    流量加密之:使用Openssl反弹加密shell 目录 流量加密之:使用Openssl反弹加密shell 1 背景 2 生成SSL证书 3 Linux平台下 3.1 在Kali上使用 OpenSSL ...

  2. slate源码解析(二)- 基本框架与数据模型

    源码架构 首先来看下最核心的slate包下的目录: 可以看到,作为一个开源富文本库,其源码是相当之少.在第一篇文章中说过,Slate没有任何开箱即用的功能,只提供给开发者用于构建富文本所需的最基本的一 ...

  3. div 元素内容超出可通过鼠标滚轮实现横向滚动

    移动端中的元素内容超出时,对容器设置overflow-x: auto就可以通过手势水平移动.但是 PC 端只能通过鼠标滚轮上下滑动,而不能水平移动. 只需要给元素添加一个监听鼠标滚轮事件,上下滑动时修 ...

  4. LeetCode-1606 找到处理请求最多的服务器

    来源:力扣(LeetCode)链接:https://leetcode-cn.com/problems/find-servers-that-handled-most-number-of-requests ...

  5. odoo Web Controllers 学习总结

    环境 odoo-14.0.post20221212.tar Web Controllers Controllers 控制器需要提供可扩展性,就像Model,但不能使用相同的机制,因为先决条件(已加载模 ...

  6. NLP知识栈

  7. 实践:腾讯云IM搭建应用内类微信社交聊天模块

    社交模块是目前主流应用程序最常见的功能之一.有了社交模块,用户在您的应用内,可以自由的交流互动,并添加好友,关注其他用户等等.这可在很大程度上,促进您应用程序的活跃度,吸引用户留存,获取更多新用户,并 ...

  8. Java实现台球游戏的动画实现相关代码

    package com.bjsxt;//scr中新建的包packageimport java.awt.*;import javax.swing.*;public class BallGame exte ...

  9. VUE学习-优化过渡

    过渡(优化) 可复用的过渡 <transition> 或者 <transition-group>为根的组件 Vue.component('my-special-transiti ...

  10. python 链接云端数据库/远程数据库 可以使用原始Odbc

    class MySqlOdbc: def __init__(self): self.sqlhead = None # 当前数据链接句柄 self.mycursor = None # 当前游标 &quo ...