Runtime 02 - Class(方法信息列表言询、方法緩存)

Runtime 02 - Class(方法信息列表、方法緩存)

Class 結(jié)構(gòu)圖

關(guān)于對 objc_class凉袱、class_data_bits_t芥吟、class_rw_t侦铜、class_ro_t 的解讀,請參考這里Objective-C 對象的本質(zhì) 01 - 底層實現(xiàn))钟鸵。

SEL钉稍、IMP

// 方法名(選擇器)
//
// 可以通過 @selector() 或 sel_registerName() 獲取 SEL。
// 可以通過 sel_getName() 或 NSStringFromSelector() 將 SEL 轉(zhuǎn)換為字符串棺耍。
// 不同類中的同名方法所對應(yīng)的 SEL 是相同的贡未。
//
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
// 函數(shù)指針
//
/// A pointer to the function of a method implementation. 
#if !OBJC_OLD_DISPATCH_PROTOTYPES
typedef void (*IMP)(void /* id, SEL, ... */ ); 
#else
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...); 
#endif

bucket_t、cache_t

typedef unsigned long uintptr_t;

// 方法緩存中的 Key
#if __LP64__
typedef uint32_t mask_t;  // x86_64 & arm64 asm are less efficient with 16-bits
#else
typedef uint16_t mask_t;
#endif
typedef uintptr_t cache_key_t;
// 方法緩存
struct bucket_t {
private:
    cache_key_t _key; // key 是方法的 SEL
    IMP _imp;         // 函數(shù)指針

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);
};
// 方法緩存列表
// 
// 用散列表(哈希表)來緩存調(diào)用多的方法烈掠,可以提高查找方法的速度羞秤。
// 
// _mask 為什么是 _buckets 的長度 -1 ?
// -1 能確保 (key & _mask) <= (_buckets 的長度 - 1)缸托,用來作為 _buckets 的下標(biāo)來取值左敌。
// 在 cache_t::find() 函數(shù)中可以看到計算下標(biāo)的代碼。
// 
// _buckets 的動態(tài)擴容:
// 向 _buckets 添加最后一個緩存時會進(jìn)行擴容俐镐。
// 每次擴容為原來的兩倍(2^n)矫限。
// 擴容時會將緩存清除,因為擴容后 _mask 發(fā)生了變化佩抹。
// 通過 cache_t::expand() 函數(shù)中進(jìn)行擴容叼风。
struct cache_t {
    struct bucket_t *_buckets; // 散列表
    mask_t _mask;              // 散列表的長度 -1
    mask_t _occupied;          // 已經(jīng)緩存的方法數(shù)量(<= _mask)

public:
    struct bucket_t *buckets();
    mask_t mask();
    mask_t occupied();
    void incrementOccupied();
    void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
    void initializeToEmpty();

    mask_t capacity();
    bool isConstantEmptyCache();
    bool canBeFreed();

    static size_t bytesForCapacity(uint32_t cap);
    static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);

    void expand();
    void reallocate(mask_t oldCapacity, mask_t newCapacity);
    struct bucket_t * find(cache_key_t key, id receiver);

    static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn));
};
/* Initial cache bucket count. INIT_CACHE_SIZE must be a power of two. */
enum {
    INIT_CACHE_SIZE_LOG2 = 2,
    INIT_CACHE_SIZE      = (1 << INIT_CACHE_SIZE_LOG2)
};

// 擴容列表
void cache_t::expand()
{
    cacheUpdateLock.assertLocked();
    
    // 計算新的長度(oldCapacity * 2),初始值為 4(INIT_CACHE_SIZE)
    uint32_t oldCapacity = capacity();
    uint32_t newCapacity = oldCapacity ? oldCapacity*2 : INIT_CACHE_SIZE;

    if ((uint32_t)(mask_t)newCapacity != newCapacity) {
        // mask overflow - can't grow further
        // fixme this wastes one bit of mask
        newCapacity = oldCapacity;
    }

    // 擴容并清除舊的緩存
    reallocate(oldCapacity, newCapacity);
}
// 擴容列表并清除舊的緩存
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);
    }
}
// 從散列表中查找方法緩存
bucket_t * cache_t::find(cache_key_t k, id receiver)
{
    assert(k != 0);

    bucket_t *b = buckets();         // 取出散列表(_buckets)
    mask_t m = mask();               // 取出散列表的長度 -1 的值(_mask)
    mask_t begin = cache_hash(k, m); // 計算緩存的哈希值(k & m)
    mask_t i = begin;
    do {
        if (b[i].key() == 0  ||  b[i].key() == k) {
            return &b[i];
        }
    } while ((i = cache_next(i, m)) != begin);

    // hack
    Class cls = (Class)((uintptr_t)this - offsetof(objc_class, cache));
    cache_t::bad_cache(receiver, (SEL)k, cls);
}
// 根據(jù)當(dāng)前下標(biāo)(i)計算下一個下標(biāo)(arm64 架構(gòu)中是 i - 1 或 mask)

#if __arm__  ||  __x86_64__  ||  __i386__
// objc_msgSend has few registers available.
// Cache scan increments and wraps at special end-marking bucket.
#define CACHE_END_MARKER 1
static inline mask_t cache_next(mask_t i, mask_t mask) {
    return (i+1) & mask;
}

#elif __arm64__
// objc_msgSend has lots of registers available.
// Cache scan decrements. No end marker needed.
#define CACHE_END_MARKER 0
static inline mask_t cache_next(mask_t i, mask_t mask) {
    return i ? i-1 : mask;
}

#else
#error unknown architecture
#endif

method_t棍苹、method_list_t无宿、method_array_t

// 方法信息
// 
// *types 示意:| 返回值 | 參數(shù)1 | 參數(shù)2 | ... | 參數(shù)n |
// 例如一個沒有返回值且沒有參數(shù)的方法,它的 method_t 中的 *types 為 v16@:8
// | v16  | @0 | :8  |
// | void | id | SEL |
// 兩個參數(shù)為默認(rèn)參數(shù)枢里,id 對應(yīng) self孽鸡,SEL 對應(yīng) _cmd
// 
// *types 中的數(shù)字:
// 第一個數(shù)字:所有參數(shù)一共占用多少字節(jié)
// 后面的數(shù)字:當(dāng)前參數(shù)從第幾個字節(jié)開始
struct method_t {
    SEL name;          // 方法名
    const char *types; // 包含了返回值類型、參數(shù)類型的編碼字符串
    IMP imp;           // 函數(shù)指針
};
// 方法信息列表(一維數(shù)組)
// Two bits of entsize are used for fixup markers.
struct method_list_t : entsize_list_tt<method_t, method_list_t, 0x3> {
    bool isFixedUp() const;
    void setFixedUp();

    uint32_t indexOfMethod(const method_t *meth) const {
        uint32_t i = (uint32_t)(((uintptr_t)meth - (uintptr_t)this) / entsize());
        assert(i < count);
        return i;
    }
};
// 方法信息列表(二維數(shù)組)
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>();
    }
};

Type Encoding(通過 @encode() 可以查看指定類型的字符串編碼)


最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末栏豺,一起剝皮案震驚了整個濱河市彬碱,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌奥洼,老刑警劉巖巷疼,帶你破解...
    沈念sama閱讀 217,542評論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異灵奖,居然都是意外死亡嚼沿,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評論 3 394
  • 文/潘曉璐 我一進(jìn)店門瓷患,熙熙樓的掌柜王于貴愁眉苦臉地迎上來伏尼,“玉大人,你說我怎么就攤上這事尉尾”祝” “怎么了?”我有些...
    開封第一講書人閱讀 163,912評論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長辨图。 經(jīng)常有香客問我唱矛,道長,這世上最難降的妖魔是什么楣黍? 我笑而不...
    開封第一講書人閱讀 58,449評論 1 293
  • 正文 為了忘掉前任彭则,我火速辦了婚禮,結(jié)果婚禮上鱼的,老公的妹妹穿的比我還像新娘理盆。我一直安慰自己,他們只是感情好凑阶,可當(dāng)我...
    茶點故事閱讀 67,500評論 6 392
  • 文/花漫 我一把揭開白布猿规。 她就那樣靜靜地躺著,像睡著了一般宙橱。 火紅的嫁衣襯著肌膚如雪姨俩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評論 1 302
  • 那天师郑,我揣著相機與錄音环葵,去河邊找鬼。 笑死宝冕,一個胖子當(dāng)著我的面吹牛张遭,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播地梨,決...
    沈念sama閱讀 40,193評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼菊卷,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了湿刽?” 一聲冷哼從身側(cè)響起的烁,我...
    開封第一講書人閱讀 39,074評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎诈闺,沒想到半個月后渴庆,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,505評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡雅镊,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,722評論 3 335
  • 正文 我和宋清朗相戀三年襟雷,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片仁烹。...
    茶點故事閱讀 39,841評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡耸弄,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出卓缰,到底是詐尸還是另有隱情计呈,我是刑警寧澤砰诵,帶...
    沈念sama閱讀 35,569評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站捌显,受9級特大地震影響茁彭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜扶歪,卻給世界環(huán)境...
    茶點故事閱讀 41,168評論 3 328
  • 文/蒙蒙 一理肺、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧善镰,春花似錦妹萨、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至竣稽,卻和暖如春囱怕,著一層夾襖步出監(jiān)牢的瞬間霍弹,已是汗流浹背毫别。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留典格,地道東北人岛宦。 一個月前我還...
    沈念sama閱讀 47,962評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像耍缴,于是被迫代替她去往敵國和親砾肺。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,781評論 2 354

推薦閱讀更多精彩內(nèi)容