Runtime

2024-06-12  本文已影响0人  buding_

Objective-C是一门动态性非常强的编程语言;
Objective-C的动态性是由Runtime API提供的;
Runtime API提供的接口基本都是C语言的,源码由C/C++ /汇编语言编写;

isa详解

isa的数据结构

在arm64架构之前,isa就是普通的指针,存储着class/meta-class的地址;
在arm64架构后,对isa进行了优化,变为了union结构,使用位域来存储更多的信息;

union isa_t
{
  Class cls;
  uintptr_t bits;
  struct {
    #define ISA_MASK 0x000000fffffff8U
    ....
      uintptr_t nonpointer : 1;  //0,代表普通指针 1,代表优化过 使用位域
      uintptr_t has_assoc : 1;  //是否设置过关联对象,如果没有释放会更快
      uintptr_t has_cxx_dtor : 1;  //是否有c++的析构函数,如果没有释放会更快
      uintptr_t shiftcls : 33;//存储着class meta-class对象的内存地址
      uintptr_t magic : 6; //用于调试时分辨对象是否完成初始化  
      uintptr_t weakly_referenced : 1; //是否有被弱引用,如果没有释放会更快         
      uintptr_t deallocating : 1;  //对象是否正在释放    
      uintptr_t has_sidetable_rc  : 1;  //引用计数器是否过大,如果过大 引用计数会存储在一个Slide Table中        
      uintptr_t extra_rc : 19;  //里面存储的值是引用计数器-1
  }
}

对isa进行 &ISA_MASK的运算后才得到class/meta-class的地址;
使用不同的掩码即可得到不同的信息;
故优化后isa能存储了更加多的信息

tip: 对于class/meta-class 的地址最后三位都是0;
通过lldb中使用: p/x person->isa  //获取地址信息
x 0x...  //获取存储的信息

class的结构

class中主要有 isa、 superclass、 属性、 对象方法、 协议、 成员变量
meta-class其实是一种特殊的class,结构与class一致

struct objc_class
{
  Class isa;
  Class superclass;
  cache_t cache;  //方法缓存
  class_data_bits_t bits;  //用于存储具体的类信息
}
通过bits & FAST_DATA_MASK得到class_rw_t
struct  class_rw_t 
{
  uint32_t flags;
  uint32_t version;
  const class_ro_t *ro;
  method_list_t *methods;  //方法列表,二维列表,包含了分类
  property_list_t *propertries;  //属性列表,二维列表,包含了分类
  const protocol_list_t *protocols;  //协议列表,二维列表,包含了分类
  Class firstSubclass;
  Class nextSiblingClass;
  char *demangledName;
}
struct class_ro_t
{
  uint32_t flags;
  uint32_t instanceStart;
  uint32_t instanceSize;  //instance占用的内存空间
  const uint8_t * ivarLayout;
  const char *name;  //类名
  method_list_t *baseMethodList; //方法列表,一维数组
  property_list_t *baseProtocols;  //协议列表,一维数组
  const ivar_list_t * ivars; //成员变量列表

}
详细看看method_list_t里面的信息
struct method_t 
{
  SEL name;  //函数名
  const char *types;  //编码(包含函数的返回值 参数等信息)
  IMP imp; //指向函数的指针
}
SEL代表方法/函数名,一般叫做选择器,底层结构跟char *类似
可以通过sel_getName和NSStringFromSelector()转成字符串
不同类中相同名字的方法,所对应的方法选择器是相同的
objc_class中的cache_t cache 是用来缓存曾经调用的方法,它的结构是一个散列表
struct cache_t
{
  struct bucket_t *buckets; //散列表
  mask_t _mask;  //散列表长度-1
  mask_t _occupied; //已经缓存方法数量
}

struct bucket_t {
  cache key_t _key; //SEL作为key  
  IMP _imp;  //函数的内存地址
}

存储时, 
使用@selector(methodName) & _mask 得出索引值,将bucket_t存入该索引下;
如果该索引下已经存在bucket_t,则索引-1地寻找下一个为空的
缺点:通过牺牲空间来换取效率

获取时,
通过进行 @selector(methodName) & _mask 得出匹配方法的索引; 
! 当得出相同的索引 则索引-1 继续寻找匹配的方法;

同时这也是散列表构建的核心(散列表,也称为哈希表或哈希映射)。

msg_send的原理

msg_send就是OC中方法调用的底层实现,即OC的消息机制,给receiver(方法调用者)发送一条消息(selector方法名)
msg_send有三大阶段:消息发送、动态方法解析、消息转发;

  1. 消息发送
对象方法: 
  先通过isa指针找到类对象,从类对象的方法列表中寻找匹配的方法;
  若类对象中找不到,则通过superclass指针找到类对象的父类,从其方法列表中寻找匹配的方法;
  当全部父类均未找到匹配的方法,则进行下一个阶段-动态方法解析

类方法:
  先通过isa指针找到元类对象,从元类对象的方法列表中寻找匹配的类方法;
  若元类对象中找不到,则通过superclass指针找到元类对象的父类,从其方法列表中寻找匹配的方法;
  当全部父类均未找到匹配的方法,则进行下一个阶段-动态方法解析
消息发送阶段流程图.png
  1. 动态方法解析
    通过开发者实现 +resolveInstanceMethod / +resolveClassMethod进行动态添加方法;
    然后再次走一遍消息发送流程;
//开发者手动实现 --
+ (BOOL)resolveInstanceMethod:(SEL)sel {
    if (sel == @selector(test)) {
        // 获取其他方法
        Method otherMethod = class_getInstanceMethod(self, @selector(other));
        //动态添加test方法实现
        class_addMethod(self, sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod));
        
        return YES;
    }
    return [super resolveInstanceMethod:sel];
}
+ (BOOL)resolveClassMethod:(SEL)sel {
    if (sel == @selector(test2)) {
        //获取元类
        Class metaClass = objc_getMetaClass("Person");
        //获取其他方法
        Method otherMethod = class_getClassMethod(self, @selector(other2));
        //动态添加实现
        class_addMethod(metaClass, sel, method_getImplementation(otherMethod), method_getTypeEncoding(otherMethod));
        return YES;
    }
    return [super resolveClassMethod:sel];
}
  1. 消息转发
    通过开发者实现 forwardingTargetForSelector 返回处理该消息的其他对象;
    若无返回或者无处理,则调取 methodSignatureForSelector返回处理该消息的方法签名,以及 forwardInvocation如何处理
1-消息转发的第一层处理
- (id)forwardingTargetForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [[Cat alloc] init];
    }
    return [super forwardingTargetForSelector:aSelector];
}
2-消息转发的第二层处理
//NSMethodSignature方法签名,包含返回值类型、参数类型
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector {
    if (aSelector == @selector(test)) {
        return [[[Cat alloc] init] methodSignatureForSelector:@selector(test)];
    }
    return [super methodSignatureForSelector:aSelector];
}
//NSInvocation封装一个方法调用,包括方法调用者、方法名、方法参数
- (void)forwardInvocation:(NSInvocation *)anInvocation {
    [anInvocation invokeWithTarget:[[Cat alloc] init]];
}

对于类方法的消息转发,以上方法均有对应类方法(流程一致)
消息转发流程图.png

super

super调用方法时,底层实现是调取 obj_msgSendSuper({self, [Person class]}, @selector(xxx)),故方法的接收者还是self

@implementation Student

- (instancetype)init {
    if (self = [super init]) {
        NSLog(@"%@",[self class]);  // Student
        NSLog(@"%@",[self superclass]); // Person
        //obj_msgSendSuper({self, [Person class]}, @selector(class))
        NSLog(@"%@",[super class]); // Student
        NSLog(@"%@",[super superclass]); // Person
    }
    return self;
}

@end


@implementation NSObject
- (Class)class {
    return object_getClass(self);
}
- (Class)superclass {
    return class_getSuperclass(object_getClass(self));
}

class

Person *p = [[Person alloc] init];
NSLog(@"%d",[p isKindOfClass:[Person class]]);  //1
NSLog(@"%d",[p isMemberOfClass:[Person class]]);    //1

NSLog(@"%d",[Person isKindOfClass:[Person class]]); //0
NSLog(@"%d",[Person isMemberOfClass:[Person class]]);   //0
        
NSLog(@"%d",[Person isKindOfClass:[NSObject class]]);   //1 -- 基类的元类的superclass = 基类
NSLog(@"%d",[Person isMemberOfClass:object_getClass(Person.class)]);    //1

@interface Person : NSObject
@property (nonatomic, copy) NSString *name;
- (void)print;
@end
@implementation Person
- (void)print {
    NSLog(@"my name is %@",self.name);
}
@end

- (void)viewDidLoad {
    [super viewDidLoad];
    // Do any additional setup after loading the view.
    NSString *v = @"123";
    id cls = [Person class];
    void *obj = &cls;
    [(__bridge id)obj print];
    //输出 ->  my name is 123
}

看完上面代码,我们提出两个问题:

q1:
正常的对象调用方法,
 Person *p = [[Person alloc] init];
 [p print];
 这里面 p 指向-> Person的对象, 包括[isa _name], 其中前八位就是isa
 再通过 isa 指向 -> Person class , 从而找到print方法进行执行,
 简化而言走向如下 p -> isa -> Person class -> print
 
 而
 id cls = [Person class];
 void *obj = &cls;
 obj 调用方法时,取出其中前八位恰恰就是cls, 而cls又为 Person class的地址, cls充当了isa的作用,
 obj -> cls -> Person class
 
 通过类比可得出第二种方式本质与正常对象调用一致,故可以执行print成功

q2
栈空间中,分配的地址是从高到低的
 - (void)test {
     NSString *a = @"1"; //0x16d9abaa8
     NSString *b = @"2"; //0x16d9abaa0
     NSString *c = @"3"; //0x16d9aba98
     NSLog(@"%p %p %p", &a, &b, &c);
 }
 
 2 对象调用方法,通过地址去取成员变量
 Person *p的对象中, 前8位是isa 后8位是_name;
 故取self.name时,通过isa指针+8访问到_name;
 
 3 例子中代码
 NSString *v = @"123";
 id cls = [Person class];
 void *obj = &cls;
 所创建的地址值, obj --+8--> cls --+8--> v
 故print方法中访问self.name时, 取isa+8的地址,即cls+8的地址 = NSString *v

runtime应用

//动态创建一个类
Class objc_allocateClassPair(Class superclass, const char *name, size_t extraBytes)
//注册一个类
void objc_registerClassPair(Class cls)
//销毁一个类
void objc_disposeClassPair(Class cls)

//设置isa指向的Class
void object_setClass(id obj, Class cls)
//获取isa指向的class
void object_getClass(id obj)

//判断一个OC对象是否为Class
BOOL object_isClass(id obj)
//判断一个OC对象是否为MetaClass
BOOL object_isMetaClass(id obj)
//获取父类
Class class_getSuperclass(Class cls)

-- 实例变量
//获取一个实例变量
Ivar class_getInstanceVariable(Class cls, const char *name)

//拷贝实例变量列表(最后需要调用free释放)
Ivar *class_copyIvarList(Class cls, unsigned int* outCount)
--想要查看某个类的内部成员可以使用这个方法

//设置和获取成员变量
void object_setIvar(id obj, Ivar ivar, id value)
id object_getIvar(id obj, Ivar ivar)

//动态添加成员变量(已经注册的类是不能动态添加成员变量)
BOOL class_addIvar(Class cls, const char* name, size_t size, uint8_t alignment, const char *types)

//获取成员变量的相关信息
const char* ivar_getName(Ivar v)
const char *ivar_getTypeEncoding(Ivar v)

-- 方法
//获取一个实例方法 类方法
Method class_getInstanceMethod(Class cls, SEL name)
Method class_getClassMethod(Class cls, SEL name)

//方法实现相关
IMP class_getMethodImplementation(Class cls, SEL name)
IMP method_setImplementation(Method m, IMP imp)
void method_exchangeImplementation(Method m1, Method m2)

//拷贝方法列表(最后需要调用free释放)
Method *class_copyMethodList(Class cls, unsinged int *outCount)

//动态添加方法
BOOL class_addMethod(Class cls, SEL name, IMP imp, const char *types)

//动态替换方法
BOOL class_replaceMethod(Class cls, SEL name, IMP imp, const char *types)

//
SEL method_getName(Method m)
IMP method_getImplementation(Method m)
const char* method_getTypeEncoding(Method m)
unsigned int method_getNumberOfArguments(Method m)
char *method_copyReturnType(Method m)
char *method_copyArgumentType(Method m, unsigned int index)

-- 选择器相关
const char *sel_getName(SEL sel)
SEL sel_registerName(const char *str)
//用block作为方法的实现
IMP imp_implementationWithBlock(id block)
id imp_getBlock(IMP anImp)
BOOL imp_removeBlock(IMP anImp)
什么是runtime? 平时项目中有用过么?

具体的应用:

上一篇 下一篇

猜你喜欢

热点阅读