Runtime 02 - Class(方法信息列表、方法缓存)
Class 结构图
关于对 objc_class、class_data_bits_t、class_rw_t、class_ro_t 的解读,请参考这里Objective-C 对象的本质 01 - 底层实现)。
SEL、IMP
// 方法名(选择器)
//
// 可以通过 @selector() 或 sel_registerName() 获取 SEL。
// 可以通过 sel_getName() 或 NSStringFromSelector() 将 SEL 转换为字符串。
// 不同类中的同名方法所对应的 SEL 是相同的。
//
/// An opaque type that represents a method selector.
typedef struct objc_selector *SEL;
// 函数指针
//
/// 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; // 函数指针
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);
};
// 方法缓存列表
//
// 用散列表(哈希表)来缓存调用多的方法,可以提高查找方法的速度。
//
// _mask 为什么是 _buckets 的长度 -1 ?
// -1 能确保 (key & _mask) <= (_buckets 的长度 - 1),用来作为 _buckets 的下标来取值。
// 在 cache_t::find() 函数中可以看到计算下标的代码。
//
// _buckets 的动态扩容:
// 向 _buckets 添加最后一个缓存时会进行扩容。
// 每次扩容为原来的两倍(2^n)。
// 扩容时会将缓存清除,因为扩容后 _mask 发生了变化。
// 通过 cache_t::expand() 函数中进行扩容。
struct cache_t {
struct bucket_t *_buckets; // 散列表
mask_t _mask; // 散列表的长度 -1
mask_t _occupied; // 已经缓存的方法数量(<= _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);
}
// 根据当前下标(i)计算下一个下标(arm64 架构中是 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 示意:| 返回值 | 参数1 | 参数2 | ... | 参数n |
// 例如一个没有返回值且没有参数的方法,它的 method_t 中的 *types 为 v16@:8
// | v16 | @0 | :8 |
// | void | id | SEL |
// 两个参数为默认参数,id 对应 self,SEL 对应 _cmd
//
// *types 中的数字:
// 第一个数字:所有参数一共占用多少字节
// 后面的数字:当前参数从第几个字节开始
struct method_t {
SEL name; // 方法名
const char *types; // 包含了返回值类型、参数类型的编码字符串
IMP imp; // 函数指针
};
// 方法信息列表(一维数组)
// Two bits of entsize are used for fixup markers.
struct method_list_t : entsize_list_tt {
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;
}
};
// 方法信息列表(二维数组)
class method_array_t : public list_array_tt {
typedef list_array_tt Super;
public:
method_list_t **beginCategoryMethodLists() {
return beginLists();
}
method_list_t **endCategoryMethodLists(Class cls);
method_array_t duplicate() {
return Super::duplicate();
}
};
Type Encoding(通过 @encode() 可以查看指定类型的字符串编码)