runtime
工作中比较重要 动态性比较强
提供了一套C语言的api 源码由C C++ 汇编语言编写实现
isa 指针
介绍
-
在arm64之前 isa 就是一个普通的指针,存储着Class Meta-Class 对象的内存地址
-
在arm64之后是将isa & ISA_MASK 找到真正的地址
- 现在是一个union 共用体 {class,bits,struct}使用位域来存储更多的信息 - 结构体支持位域
union isa_t
{
isa_t() { }
isa_t(uintptr_t value) : bits(value) { }
Class cls;
uintptr_t bits;
struct {
uintptr_t nonpointer : 1;
uintptr_t has_assoc : 1;
uintptr_t has_cxx_dtor : 1;
uintptr_t shiftcls : 33; // MACH_VM_MAX_ADDRESS 0x1000000000
uintptr_t magic : 6;
uintptr_t weakly_referenced : 1;
uintptr_t deallocating : 1;
uintptr_t has_sidetable_rc : 1;
uintptr_t extra_rc : 19;
};
}
& 按位与 取出设置特定位置的值
设置源码为 0111 MJ 老师的帅富高 改成 帅 不富有 高 0101
- 看看是不是富有 0101 & 0010 (想取出最高位那么就是最高位为1 其他的位为0)那么得出来的值是 0000 不富有
- 设置这个人不帅将某一位置为0 将这个位置置为0 其他位置为1 : 0101 & 1011 = 0001
- |按位或 设置特定位置的值 设置这个人是富有的 0101 | 0010 = 0111 的出来的结果
union 值 | 中文说明 |
---|---|
nonpointer | 0:代表普通的指针,存储着Class Meta-Class对象的内存地址1:代表优化过,使用位域存储更多的信息 |
has_assoc | 是否有设置过关联的对象,如果没有,释放时更快(category 增加属性) |
has_cxx_dtor | 是否有C++的析构函数(.cxx_destruct),如果没有,释放时更快 |
shiftcls | 存储着Class,Meta-Class对象的内存地址信息 |
magic | 用于在调试时,分辨对象完成初始化 |
weakly_referenced | 是否被若引用指向过,如果没有释放时更快 |
deallocating | 对象是否正在释放 |
has_sidetable_rc | 引用计数器是否过大无法存储在isa中,如果为1,那么引用计数会存储在一个叫SideTable的类的属性中 |
extra_rc | 里面存储的值时引用计数器减1 |
class 结构
struct objc_class : objc_object {
// Class ISA;
Class superclass;
cache_t cache; // formerly cache pointer and vtable 方法缓存
class_data_bits_t bits; // class_rw_t * plus custom rr/alloc flags 用于获取具体的累的信息
}
class_rw_t 可读可写
class_rw_t 里面包含着 methods、properties、protocols是二维数组,是可读可写的,包含了累的初始化内容,分类的内容
class 中的bits & FAST_DATA_MASK ->
class_rw_t* data() {
return (class_rw_t *)(bits & FAST_DATA_MASK);
}
class_rw_t ->
struct class_rw_t {// 可读可写的
// Be warned that Symbolication knows the layout of this structure.
uint32_t flags;
uint32_t version;
const class_ro_t *ro; // 不可写的 只读的
method_array_t methods; // 方法列表里面是一个二维数组 类方法 分类方法 //【method_list_t,method_list_t,method_list_t】 method_list_t:[method_t,method_t,method_t] 下面有解释
property_array_t properties;//【property_list_t,property_list_t,property_list_t】property_list_t:[property_t,property_t,property_t]
protocol_array_t protocols; // 【protocol_list_t,protocol_list_t,protocol_list_t】protocol_list_t:[protocol_ref_t,protocol_ref_t,protocol_ref_t]
Class firstSubclass;
Class nextSiblingClass;
char *demangledName;
}
method_array_t -> class method_array_t : public list_array_tt<method_t, method_list_t> //
struct method_t { // 方法信息
SEL name;
const char *types;
IMP imp;
}
struct property_t { //属性
const char *name;
const char *attributes;
};
typedef uintptr_t protocol_ref_t;// typedef unsigned long uintptr_t; 协议
class_ro_t 只读
class_ro_t 里面的baseMethodList、baseProtocols、ivars、baseProperties 是一维数组、是只读的、包含了类的初始化内容(仅仅是类里面定义的,不包含分类啥的)
//class_ro_t -> readOnly 可读不可写
struct class_ro_t {
uint32_t flags;
uint32_t instanceStart;
uint32_t instanceSize;
#ifdef __LP64__
uint32_t reserved;
#endif
const uint8_t * ivarLayout;
const char * name; // 类名
method_list_t * baseMethodList;// 【method_t,method_t,method_t】
protocol_list_t * baseProtocols;
const ivar_list_t * ivars; // 成员变量列表
const uint8_t * weakIvarLayout;
property_list_t *baseProperties;
method_list_t *baseMethods() const {
return baseMethodList;
}
};
程序启动时会将 class_ro_t 里面的方法和分类添加到class_rw_t 中
/* realizeClass
* Performs first-time initialization on class cls,
* including allocating its read-write data.
* Returns the real class structure for the class.
* Locking: runtimeLock must be write-locked by the caller
*实现类
*对类cls进行首次初始化,
*包括分配其读写数据。
*返回该类的真实类结构。
*锁定:runtimeLock必须由调用方写锁定
生成class_rw_t
*/
static Class realizeClass(Class cls)
method_t 是对方法\函数的封装
struct method_t { // 方法信息
SEL name; // 函数名
const char *types; // 编码(返回值类型,参数类型)
IMP imp;// 指向函数的指针(函数指针) +load() 就是直接调用的指针,不是消息转发
}
SEL
// IMP 代表函数的具体实现
typedef id _Nullable (*IMP)(id _Nonnull, SEL _Nonnull, ...);
// SEL 代表方法\函数,一般叫做选择器,底层结构和char * 类似
typedef struct objc_selector *SEL; // 但是源码中没有对objc_selector的解释
//GNU OC 但是和iOS的上的其实是不一样的
typedef struct objc_selector *SEL;
struct objc_selector
{
void *sel_id;
const char *sel_types;
};
- 获取SEL 的方法 @selector(init); sel_registerName("init");
- 将sel转成字符串 sel_getName(); NSStringFromSelector()
- 不同类中相同名字的方法,所对应的方法选择器是相同的
**types ** 编码(返回值类型,参数类型)Type Encoding 简书
例如 "i24@0:8i16f20"
-(int)test:(int)age height:(float)height{ ->后面其实是有self 和 _cmd 的
}
-(int)test:(id)self cmd:(SEL)cmd age(int):age height:(float)height{
}
i | 24 | @ | 0 | : | 8 | i | 16 | f | 20 |
---|---|---|---|---|---|---|---|---|---|
返回值int | 所有的字节的总和 id +sel+int+float | id 类型就是那个self(8字节) | 从0开始 | sel(8字节) | 从第8个字节开始 | int(4字节) | 从16个字节开始 | float(4字节) | 从20个字节开始 |
返回值 | 参数1 | 参数2 | .... | 参数n |
---|---|---|---|---|
@encode(char) c
@encode(int) i
@encode(long) q
@encode(long long) q
@encode(unsigned char) C
@encode(unsigned int) I
@encode(unsigned short) S
@encode(unsigned long) Q
@encode(unsigned long long) Q
@encode(float) f
@encode(float *) ^f
@encode(double) d
@encode(double *) ^d
@encode(BOOL) B
@encode(void) v
@encode(void *) ^v
@encode(char *) *
@encode(NSObject) {NSObject=#}
@encode(NSObject *) @
@encode([NSObject class]) #
@encode(SEL) :
@encode(intArray) [3i]
@encode(floatArray) [3f]
@encode(Struct) {_struct=sqQ^d}
@encode(NSError) {NSError=#^vq@@}
@encode(NSError ** ) ^@
方法缓存
Class 内部结构中有个方法缓存(cache_t),用散列表来缓存曾经调用过的方法,可以提高方法的查找速度
牺牲空间换时间(散列表也是hash表)
struct cache_t {
struct bucket_t *_buckets; // 散列表[bucket_t,bucket_t,bucket_t,....]
mask_t _mask; // 散列表长度 -1
mask_t _occupied; // 已经缓存的方法的数量
};
struct bucket_t {
cache_key_t _key; // SEL 作为key
IMP _imp; // 函数的内存地址
}
// 存放地址为 sel&mask
bucket_t * cache_t::find(cache_key_t k, id receiver)
{
assert(k != 0);
bucket_t *b = buckets();
mask_t m = mask();
mask_t begin = cache_hash(k, m);
mask_t i = begin;
do {
if (b[i].key() == 0 || b[i].key() == k) {// 如果找到的key 是想要的
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);
}
static inline mask_t cache_next(mask_t i, mask_t mask) {
return i ? i-1 : mask; // 将值减1 如果是0 那么就变成mak 从最后再往前找
}
void cache_t::expand() //扩展方法列表 ,将之前的缓存清除掉(不会放回到新的中),然后再散列
{
cacheUpdateLock.assertLocked();
uint32_t oldCapacity = capacity();
uint32_t newCapacity = oldCapacity ? oldCapacity*2 : INIT_CACHE_SIZE; // 每次乘2倍 默认为4
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);// 之前的缓存清除掉,然后再散列
}
enum {
INIT_CACHE_SIZE_LOG2 = 2,
INIT_CACHE_SIZE = (1 << INIT_CACHE_SIZE_LOG2) //4
};
// 取值的主要函数
static inline mask_t cache_hash(cache_key_t key, mask_t mask)
{
return (mask_t)(key & mask);
}
方法调用流程
image//OC 的方法调用:消息机制,给方法调用者发送消息
//消息接收者(receiver)
//消息名称
objc_msgSend()
- 消息发送
- 动态方法解析
- 消息转发
如果方法找不到方法最后会报错:
unrecognized selector send to instance
消息发送流程
image// 二分查找 如果方法已经进行了排序,那么就进行二分查找
for (count = list->count; count != 0; count >>= 1) {
probe = base + (count >> 1);
uintptr_t probeValue = (uintptr_t)probe->name;
if (keyValue == probeValue) {
// `probe` is a match.
// Rewind looking for the *first* occurrence of this value.
// This is required for correct category overrides.
while (probe > first && keyValue == (uintptr_t)probe[-1].name) {
probe--;
}
return (method_t *)probe;
}
if (keyValue > probeValue) {
base = probe + 1;
count--;
}
}
动态方法解析
image- (void)other {
NSLog(@"%s", __func__);
}
void c_other(id self, SEL _cmd)
{
NSLog(@"%s", __func__);
}
struct method_t { // 方法信息
SEL name; // 函数名
const char *types; // 编码(返回值类型,参数类型)
IMP imp;// 指向函数的指针(函数指针) +load() 就是直接调用的指针,不是消息转发
};
+ (BOOL)resolveClassMethod:(SEL)sel {
// 动态添加方法实现
if (sel == @selector(test)) {
// 对象方法添加的位置是类对象上
// 获取其他方法
// 注意一定要加元类的
{ // 方法1
struct method_t *method = (struct method_t *)class_getInstanceMethod(self, @selector(other));
class_addMethod(object_getClass(self), sel, method->imp, method->types);
}
{ // 方法2
Method otherMethod = class_getInstanceMethod(self, @selector(other));
class_addMethod(object_getClass(self), sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod));
}
{// 方法3
class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
}
// 返回yes 代表有动态添加方法 // 其实返回NO也是可以的 因为源码中拿着返回值仅仅是做的打印
return YES;
}
return [super resolveInstanceMethod:sel];
}
+ (BOOL)resolveInstanceMethod:(SEL)sel {
// 动态添加方法实现
if (sel == @selector(test)) {
// 对象方法添加的位置是类对象上
// 获取其他方法
{ // 方法1
struct method_t *method = (struct method_t *)class_getInstanceMethod(self, @selector(other));
class_addMethod(self, sel, method->imp, method->types);
}
{ // 方法2
Method otherMethod = class_getInstanceMethod(self, @selector(other));
class_addMethod(self, sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod));
}
{// 方法3
class_addMethod(self, sel, (IMP)c_other, "v16@0:8");
}
// 返回yes 代表有动态添加方法 // 其实返回NO也是可以的 因为源码中拿着返回值仅仅是做的打印
return YES;
}
return [super resolveInstanceMethod:sel];
}
网友评论