当前位置:网站首页>Runtime——methods成员变量,cache成员变量
Runtime——methods成员变量,cache成员变量
2022-06-25 06:39:00 【chabuduoxs】
类,元类的methods成员变量
在之前分析类的时候,类里面的methods存储着该类所有的实例方法信息,那么它具体是怎么存储的?
class method_array_t :
public list_array_tt<method_t, method_list_t>
{
typedef list_array_tt<method_t, method_list_t> Super;
public:
method_list_t **beginCategoryMethodLists() {
return beginLists();
}
method_list_t **endCategoryMethodLists(Class cls);
method_array_t duplicate() {
return Super::duplicate<method_array_t>();
}
};
// MARK: - method结构声明
struct method_t {
SEL name;//SEL
const char *types;//方法参数和类型
MethodListIMP imp;//imp
struct SortBySELAddress :
public std::binary_function<const method_t&,
const method_t&, bool>
{
bool operator() (const method_t& lhs,
const method_t& rhs)
{
return lhs.name < rhs.name; }
};
};
查看源码发现其实methods是一个数组指针,这个数组的大小为(N + 1)* 8个字节(N为分类个数),也就是说这个数组里面存着一些一维数组,指向真正的实例方法列表,也就是分类1的实例方法列表,类本身的方法列表等等。方法列表里面放着一个个实例方法method_t,看看他的定义。
// MARK: - method结构声明
struct method_t {
SEL name;//SEL
const char *types;//方法参数和类型
MethodListIMP imp;//imp
struct SortBySELAddress :
public std::binary_function<const method_t&,
const method_t&, bool>
{
bool operator() (const method_t& lhs,
const method_t& rhs)
{
return lhs.name < rhs.name; }
};
};
typedef struct method_t *Method; // 方法声明
// 方法的本质就是一个method_t结构体指针,它可以指向任何一个方法。
可以发现里面就三个指针,所以它只占用了24个字节,这些内存都是在静态区的。元类的methods同理,只不过保存的是类方法列表。
下面分析一下这三个成员变量。
SEL:方法选择器,跟方法名一一对应,是一个方法的唯一标识,可以当作方法名看待,之前使用的@selector(方法名)就是获得一个方法选择器。types:类型编码字符串,包含了方法的参数和返回值信息,编码中第一个值代表返回值类型,后面字母依次表示该方法的各个参数类型。第一个数字代表所有参数占用总内存,后面的数字代表各参数内存地址的偏移量。IMP:函数指针,存储着一个地址,指向该方法在代码区的具体实现。
类,元类的cache成员变量
当一个对象接受到消息时,会根据它的isa指针找到它所属的类,然后根据类的methods找到所有的方法列表,然后依次遍历这些方法列表来查找要执行的方法。在实际情况中,一个对象只有一部分方法是常用的,其余方法用的频率很低,如果对象每接受一次消息就要遍历一次所有的列表,性能肯定很差。
类的cache成员变量就是用来解决这个问题的。
系统每次调用一次方法,就会将这个方法存储到cache中,下次再调用方法就优先从cache中查找。找不到再去methods里面找,大大提高了方法查找的效率。
看看cache源码。
struct cache_t {
struct bucket_t *_buckets;
mask_t _mask;
mask_t _occupied;
}
// MARK: - bucket_t声明结构
struct bucket_t {
private:
// IMP-first is better for arm64e ptrauth and no worse for arm64.
// SEL-first is better for armv7* and i386 and x86_64.
#if __arm64__
MethodCacheIMP _imp;
cache_key_t _key;
#else
cache_key_t _key;
MethodCacheIMP _imp;
#endif
public:
inline cache_key_t key() const {
return _key; }
inline IMP imp() const {
return (IMP)_imp; }
inline void setKey(cache_key_t newKey) {
_key = newKey; }
inline void setImp(IMP newImp) {
_imp = newImp; }
void set(cache_key_t newKey, IMP newImp);
};
分析一下成员变量
- _buckets:方法缓存散列表
- _mask:散列表长度 - 1
- _occupied:缓存方法数量
可以看出散列表里面的元素不直接是method_t,而是bucket_t,点开它的结构可以发现,它有两个成员变量IMP和cache_key_t.IMP就是一个函数指针,令一个忙猜是函数标识。
下面看看apple如何实现这个散列表。
// Class points to cache. SEL is key. Cache buckets store SEL+IMP.
// Caches are never built in the dyld shared cache.
// 可以在这里的sel is key看出cache_key_t等同于唯一标识SEL,cahe散列表存储了IMP和SEL
// 缓存不会构建在dyld共享缓存中
static inline mask_t cache_hash(cache_key_t key, mask_t mask)
{
// 这里的散列算法很简单,就是用key&长度-1获得index
// SEL是方法唯一标识
return (mask_t)(key & mask);
}
cache_hash冲突
在实际过程中,有可能遇见这样的问题,不同的sel & n - 1后得到了相同的index,那么会产生数据冲突。这时如何处理?
// 散列表读取
bucket_t * cache_t::find(cache_key_t k, id receiver)
{
assert(k != 0);
// 获取散列表和长度-1
bucket_t *b = buckets();
mask_t m = mask();
// 通过散列算法得到某个长度的索引
mask_t begin = cache_hash(k, m);
mask_t i = begin;
do {
// 读取index的元素对比SEL,判断是否和我们需要的相等,返回
// 或者找到空闲内存,说明第一次调用,存入
if (b[i].key() == 0 || b[i].key() == k) {
return &b[i];
}
} while ((i = cache_next(i, m)) != begin);
// 否则Index - 1,遍历散列表,直到读取到想要的SEL
// hack
Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
// 判断一些错误情况
cache_t::bad_cache(receiver, (SEL)k, cls);
}
可以看见这里需要对比index处是否空闲或者元素SEL是否和我们搜索的相等,如果没找到我们就会index - 1,遍历散列表,直到找到空闲的内存,或者真正的方法。
读取我们直接根据index拿方法,不需要遍历。
cache散列表扩容
void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity)
{
bool freeOld = canBeFreed();
bucket_t *oldBuckets = buckets();
// 开辟新的散列表
bucket_t *newBuckets = allocateBuckets(newCapacity);
// Cache's old contents are not propagated.
// This is thought to save cache memory at the cost of extra cache fills.
// fixme re-measure this
assert(newCapacity > 0);
assert((uintptr_t)(mask_t)(newCapacity-1) == newCapacity-1);
setBucketsAndMask(newBuckets, newCapacity - 1);
if (freeOld) {
// 释放旧的散列表,清空其缓存
cache_collect_free(oldBuckets, oldCapacity);
cache_collect(false);
}
}
当散列表的内存不够用时,系统进行两倍扩容。
本文参考——参考文章
边栏推荐
- Distributed quorum NWR of the alchemy furnace of the Supreme Master
- Authentique Photoshop 2022 expérience d'achat partage
- PI Ziheng embedded: This paper introduces the multi-channel link mode of i.mxrt timer pit and its application in coremark Test Engineering
- Can I open a stock account with a compass? Is it safe?
- [Batch dos - cmd Command - Summary and Summary] - External Command - cmd Download Command, wget Command
- 栅格地图(occupancy grid map)构建
- Genuine photoshop2022 purchase experience sharing
- 韩信大招:一致性哈希
- 不同路径II[针对DFS的动态规划改进]
- IAR compiler flashback
猜你喜欢

JMeter introduction practice ----- use of global variables and local variables

Vscode official configuration synchronization scheme

ELK + filebeat日志解析、日志入库优化 、logstash过滤器配置属性

【批处理DOS-CMD命令-汇总和小结】-应用程序启动和调用、服务和进程操作命令(start、call、)

Classic paper in the field of character recognition: aster
![[batch dos-cmd command - summary and summary] - external command -cmd download command and packet capture command (WGet)](/img/00/5a5b081b78ad6a6c1c3a3c847dd315.png)
[batch dos-cmd command - summary and summary] - external command -cmd download command and packet capture command (WGet)

China Mobile MCU product information
![[pytest] modify the logo and parameterization in the allure Report](/img/c0/93519da008ec137c447bb11aa7b73e.png)
[pytest] modify the logo and parameterization in the allure Report

Construction of occupancy grid map

CPDA | how to start the growth path of data analysts?
随机推荐
Large funds support ecological construction, and Plato farm builds a real meta universe with Dao as its governance
用太极拳讲分布式理论,真舒服!
栅格地图(occupancy grid map)构建
对链表进行插入排序[dummy统一操作+断链核心--被动节点]
OpenMP入门
13 `bs_duixiang.tag标签`得到一个tag对象
The principle of Zener diode, what is its function?
Introduction to Sichuan Tuwei ca-is3082wx isolated rs-485/rs-422 transceiver
Evolution of Alibaba e-commerce architecture
Tupu software digital twin 3D wind farm, offshore wind power of smart wind power
Shell tips (134) simple keyboard input recorder
The perfect presentation of Dao in the metauniverse, and platofarm creates a farm themed metauniverse
C#入门教程
音频(五)音频特征提取
VectorDraw Developer Framework 10.10
Don't you know the evolution process and principle of such a comprehensive redis cluster model?
Unity3D邪门实现之GUI下拉菜单Dropdown设计无重复项
My debut is finished!
威迈斯新能源冲刺科创板:年营收17亿 应收账款账面价值近4亿
[batch dos-cmd command - summary and summary] - commands related to Internet access and network communication (Ping, Telnet, NSLOOKUP, ARP, tracert, ipconfig)