咱們在iOS底層原理之isa走位與類結構分析 中分析了objc_class
結構體中的bits
屬性,今天咱們分析cache_t
屬性。數組
cache_t
cache_t
部分源碼struct cache_t {
#if CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_OUTLINED
explicit_atomic<struct bucket_t *> _buckets;
explicit_atomic<mask_t> _mask;
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_HIGH_16
explicit_atomic<uintptr_t> _maskAndBuckets;
mask_t _mask_unused;
// How much the mask is shifted by.
static constexpr uintptr_t maskShift = 48;
// Additional bits after the mask which must be zero. msgSend
// takes advantage of these additional bits to construct the value
// `mask << 4` from `_maskAndBuckets` in a single instruction.
static constexpr uintptr_t maskZeroBits = 4;
// The largest mask value we can store.
static constexpr uintptr_t maxMask = ((uintptr_t)1 << (64 - maskShift)) - 1;
// The mask applied to `_maskAndBuckets` to retrieve the buckets pointer.
static constexpr uintptr_t bucketsMask = ((uintptr_t)1 << (maskShift - maskZeroBits)) - 1;
// Ensure we have enough bits for the buckets pointer.
static_assert(bucketsMask >= MACH_VM_MAX_ADDRESS, "Bucket field doesn't have enough bits for arbitrary pointers.");
#elif CACHE_MASK_STORAGE == CACHE_MASK_STORAGE_LOW_4
// _maskAndBuckets stores the mask shift in the low 4 bits, and
// the buckets pointer in the remainder of the value. The mask
// shift is the value where (0xffff >> shift) produces the correct
// mask. This is equal to 16 - log2(cache_size).
explicit_atomic<uintptr_t> _maskAndBuckets;
mask_t _mask_unused;
static constexpr uintptr_t maskBits = 4;
static constexpr uintptr_t maskMask = (1 << maskBits) - 1;
static constexpr uintptr_t bucketsMask = ~maskMask;
#else
#error Unknown cache mask storage type.
#endif
#if __LP64__
uint16_t _flags;
#endif
uint16_t _occupied;
public:
static bucket_t *emptyBuckets();
struct bucket_t *buckets();
mask_t mask();
mask_t occupied();
void incrementOccupied();
void setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask);
void initializeToEmpty();
unsigned capacity();
bool isConstantEmptyCache();
bool canBeFreed();
#if __LP64__
bool getBit(uint16_t flags) const {
return _flags & flags;
}
void setBit(uint16_t set) {
__c11_atomic_fetch_or((_Atomic(uint16_t) *)&_flags, set, __ATOMIC_RELAXED);
}
void clearBit(uint16_t clear) {
__c11_atomic_fetch_and((_Atomic(uint16_t) *)&_flags, ~clear, __ATOMIC_RELAXED);
}
#endif
#if FAST_CACHE_ALLOC_MASK
bool hasFastInstanceSize(size_t extra) const
{
if (__builtin_constant_p(extra) && extra == 0) {
return _flags & FAST_CACHE_ALLOC_MASK16;
}
return _flags & FAST_CACHE_ALLOC_MASK;
}
size_t fastInstanceSize(size_t extra) const
{
ASSERT(hasFastInstanceSize(extra));
if (__builtin_constant_p(extra) && extra == 0) {
return _flags & FAST_CACHE_ALLOC_MASK16;
} else {
size_t size = _flags & FAST_CACHE_ALLOC_MASK;
// remove the FAST_CACHE_ALLOC_DELTA16 that was added
// by setFastInstanceSize
return align16(size + extra - FAST_CACHE_ALLOC_DELTA16);
}
}
void setFastInstanceSize(size_t newSize)
{
// Set during realization or construction only. No locking needed.
uint16_t newBits = _flags & ~FAST_CACHE_ALLOC_MASK;
uint16_t sizeBits;
// Adding FAST_CACHE_ALLOC_DELTA16 allows for FAST_CACHE_ALLOC_MASK16
// to yield the proper 16byte aligned allocation size with a single mask
sizeBits = word_align(newSize) + FAST_CACHE_ALLOC_DELTA16;
sizeBits &= FAST_CACHE_ALLOC_MASK;
if (newSize <= sizeBits) {
newBits |= sizeBits;
}
_flags = newBits;
}
#else
bool hasFastInstanceSize(size_t extra) const {
return false;
}
size_t fastInstanceSize(size_t extra) const {
abort();
}
void setFastInstanceSize(size_t extra) {
// nothing
}
#endif
static size_t bytesForCapacity(uint32_t cap);
static struct bucket_t * endMarker(struct bucket_t *b, uint32_t cap);
void reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld);
void insert(Class cls, SEL sel, IMP imp, id receiver);
static void bad_cache(id receiver, SEL sel, Class isa) __attribute__((noreturn, cold));
};
複製代碼
從上述源碼中咱們能夠看出,cache_t
中存儲了函數方法的集合以及聲明的變量等。緩存
buket_t
類型的結構體數組,存儲方法指針地址_IMP
以及方法編號_SEL
mask_t m = capacity - 1
,能夠理解爲cache_t的緩存容量大小,因爲(capacity = MAX_CACHE_SIZE;)
中MAX_CACHE_SIZE
是2整數次冪cache_t
代碼跟蹤咱們在LGPerson類中聲明一下方法,並實現markdown
@interface LGPerson : NSObject
- (void)sayHello;
- (void)sayCode;
- (void)sayMaster;
- (void)sayNB;
+ (void)sayHappy;
@end
@implementation LGPerson
- (void)sayHello{
NSLog(@"LGPerson say : %s",__func__);
}
- (void)sayCode{
NSLog(@"LGPerson say : %s",__func__);
}
- (void)sayMaster{
NSLog(@"LGPerson say : %s",__func__);
}
- (void)sayNB{
NSLog(@"LGPerson say : %s",__func__);
}
+ (void)sayHappy{
NSLog(@"LGPerson say : %s",__func__);
}
@end
複製代碼
在main()函數中代碼實現app
LGPerson *p = [LGPerson alloc];
Class pClass = [LGPerson class];
[p sayHello];
[p sayCode];
[p sayMaster];
複製代碼
此時 咱們能夠看到cache尚未存儲方法函數
此時_buckets、_mask、_occupied均發生了變化oop
_mask = 3
,_occupied = 2
_mask = 7
,_occupied = 1
。爲何_mask
變換這麼大以及_occupied
爲何不是3?想要解決這個問題咱們要看它們是怎麼實現的。咱們從
cache_t
結構體中發現上述方法,咱們在源碼中搜索incrementOccupied
中發現了初始化cache_t,發現咱們在
方法中發現了調用的地方,咱們探究下該方法
咱們先看須要注意的兩處代碼fetch
enum {
INIT_CACHE_SIZE_LOG2 = 2,
INIT_CACHE_SIZE = (1 << INIT_CACHE_SIZE_LOG2),
MAX_CACHE_SIZE_LOG2 = 16,
MAX_CACHE_SIZE = (1 << MAX_CACHE_SIZE_LOG2),
};
複製代碼
經過INIT_CACHE_SIZE,咱們知道每次開闢的大小是2的冪,第一次是4,因此當咱們的調用一個或兩個實例方法的時候_mask = 3
ui
void cache_t::reallocate(mask_t oldCapacity, mask_t newCapacity, bool freeOld)
{
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);
}
}
複製代碼
咱們發現了setBucketsAndMask
方法,setBucketsAndMask
代碼爲this
void cache_t::setBucketsAndMask(struct bucket_t *newBuckets, mask_t newMask)
{
#ifdef __arm__
// ensure other threads see buckets contents before buckets pointer
mega_barrier();
_buckets.store(newBuckets, memory_order::memory_order_relaxed);
// ensure other threads see new buckets before new mask
mega_barrier();
_mask.store(newMask, memory_order::memory_order_relaxed);
_occupied = 0;
#elif __x86_64__ || i386
// ensure other threads see buckets contents before buckets pointer
_buckets.store(newBuckets, memory_order::memory_order_release);
// ensure other threads see new buckets before new mask
_mask.store(newMask, memory_order::memory_order_release);
_occupied = 0;
#else
#error Don't know how to do setBucketsAndMask on this architecture.
#endif
}
複製代碼
在setBucketsAndMask
方法中將_occupied = 0
初始化爲0了,所以咱們獲得每次擴容後,_occupied都從0開始再次計數
,估調用三個實例方法後_occupied = 1
atom
void cache_t::insert(Class cls, SEL sel, IMP imp, id receiver)
方法緩存調用的方法reallocate
方法開闢空間,在reallocate
方法中通setBucketsAndMask
方法將_occupied
初始化爲0,開闢大小爲4,由於_mask = capacity - 1
,其中capacity即爲開闢的大小,因此 _mask = 3;3/4
則不進行處理3/4
,則進行擴容處理,此時經過reallocate
擴容,每次擴容按照原先的兩倍來擴容,將_occupied
初始化爲0,