1. 程式人生 > >iOS方法快取-散列表

iOS方法快取-散列表

Class 內部結構中有個方法快取( cache_t ), 用散列表來快取曾經呼叫過的方法,可以提高方法的查詢速度

 

每個類都有一個自己的方法列表陣列,每次呼叫方法的時候,都會去找當前的類的方法陣列看看有沒有這個方法,如果沒找到,就去父類尋找,不過,在這些之前,最先去快取陣列cache 裡面找,如果是第一次呼叫,在方法列表裡面找到這個方法之後,會把這個方法在父類和當前累的快取數組裡面各儲存· 一份

當陣列已經裝不下的時候,陣列就會翻倍擴容,然後把之前的資料清空

怎麼儲存快取的方法:散列表技術 key-value

cache_t 的資料結構

思路 :呼叫方法的時候,通過方法名(key)去到通過isa指標找到的對應的類的快取數組裡面的散列表( struct _bucket_t *_buckets )查詢,如果找到,直接拿出對應的函式記憶體地址。直接呼叫(這就是為啥一個類裡面不能有相同名稱的方法)

 

#import <objc/runtime.h>

#import <objc/message.h>

 

NSLog(@"%p",@selector(test)); // 輸出方法的記憶體地址

objc_msgSend(person, @selector(test)); // 呼叫person類的test方法

 

散列表的資料儲存位置:

@selector(personTest) @ _mask( 散列表的長度-1 ) = 這個資料快取的位置的下標,也就是快取方法的索引,這個下標經過位運算之後,一定會小於或者等於散列表的長度-1 ,就不會出現陣列越界的情況了

 

散列表資料結構

 

列印快取的方法

 

Student *student = [[Student alloc]init];

mj_objc_class *studentClass = (__bridge mj_objc_class *)[Student class];

[student studentTest];

[student personTest];

[student studentTest];

[student personTest];

cache_t cache = studentClass->cache; // 拿到快取cache

bucket_t *buckets = cache._buckets; // 裡面是通過散列表的思路進行快取的

// mj的獲取方法

// NSLog(@"%s %p",@selector(studentTest), cache.imp(@selector(studentTest)));

// bucket_t bucket = buckets[(long long)@selector(personTest) & cache._mask ];

// NSLog(@"------------");

// NSLog(@"%s %p",bucket._key, bucket._imp);

// NSLog(@"------------");

// 列印全部快取資料

for (int i = 0 ; i <= cache._mask ; i++) {

bucket_t bucket = buckets[i];

NSLog(@"%s %p",bucket._key, bucket._imp);

}

// 直接通過 索引去除對應的bucket

// 1、 算出索引 @selector(personTest) & _mask

bucket_t bucket = buckets[(long long)@selector(studentTest) & cache._mask ];

NSLog(@"%s %p",bucket._key, bucket._imp);

 

最後:

感謝 MJ 老師的視訊教程,受益良多,有興趣的同學可以瞭解一下,MJ 底層原理開發,騰訊視訊就有,為了避免說我打廣告,連結我就不發了