OC底层知识(九) : Runtime(上)

简介: OC底层知识(九) : Runtime

一、Runtime的介绍



  • Objective-C是一门动态性比较强的编程语言,跟C、C++等语言有着很大的不同
  • Objective-C的动态性是由Runtime API来支撑的
  • Runtime API提供的接口基本都是C语言的,源码由C\C++\汇编语言编写


二、isa详解



  • 要想学习Runtime,首先要了解它底层的一些常用数据结构,比如isa指针
  • 在arm64架构之前,isa就是一个普通的指针,存储着Class、Meta-Class对象的内存地址
  • 从arm64架构开始,对isa进行了优化,变成了一个共用体(union)结构,还使用位域来存储更多的信息


image.png



  • isa详解 – 位域
  • nonpointer
  • 0,代表普通的指针,存储着Class、Meta-Class对象的内存地址
  • 1,代表优化过,使用位域存储更多的信息
  • has_assoc
  • 是否有设置过关联对象,如果没有,释放时会更快
  • has_cxx_dtor
  • 是否有C++的析构函数(.cxx_destruct),如果没有,释放时会更快
  • shiftcls
  • 着Class、Meta-Class对象的内存地址信息
  • magic
  • 用于在调试时分辨对象是否未完成初始化
  • weakly_referenced
  • 是否有被弱引用指向过,如果没有,释放时会更快
  • deallocating
  • 对象是否正在释放
  • extra_rc
  • 里面存储的值是引用计数器减1
  • has_sidetable_rc
  • 引用计数器是否过大无法存储在isa中
  • 如果为1,那么引用计数会存储在一个叫SideTable的类的属性中


  • 什么是isa?
    答:在 arm64之前isa就是一个普通的指针,里面就是直接存储的类对象元类对象的地址值,但是从arm64位之后,isa经过优化,它采用共用体的结构,将一个64位的内存数据分开来存储了很多的东西,而其中的33位来存储具体的地址值的。


三、objc_msgSend执行流程可以分为3大阶段



OC的方法调用:消息机制(objc_msgSend(id receiver, SEL selector)),给方法调用者发送消息


image.png


3.1、消息发送(其实也就是isa与superclass寻找方法(对象方法类方法))

image.png


- 如果是从 class_rw_t 中查找方法: 已经排序的二分查找,没有排序的遍历查找

- receiver通过isa指针找到receiverClass

- receiverClass通过superclass指针找到superClass


  • 3.2、动态方法解析(在消息发送机制找不到方法之后进行动态方法解析)


image.png



  • 可以实现以下方法,来动态添加方法实现
    + (BOOL)resolveInstanceMethod:(SEL)sel; (对象方法的动态添加)


+ (BOOL)resolveInstanceMethod:(SEL)sel
{
   if (sel == @selector(test)) {
       // 获取其他方法
       Method method = class_getInstanceMethod(self, @selector(other));
       // 动态添加test方法的实现
       class_addMethod(self, sel,
                       method_getImplementation(method),
                       method_getTypeEncoding(method));
       // 返回YES代表有动态添加方法
       return YES;
    }
  return [super resolveInstanceMethod:sel];
}
-(void)other{
   NSLog(@"%s",__func__);
}
  • + (BOOL)resolveClassMethod:(SEL)sel;  (类方法的动态添加)


+ (BOOL)resolveClassMethod:(SEL)sel
{
   if (sel == @selector(classTest)) {
      // 第一个参数是object_getClass(self)
      //class_addMethod(object_getClass(self), sel, (IMP)c_other, "v16@0:8");
      // 获取其他方法
      Method method = class_getClassMethod(self, @selector(classOther));
      // 动态添加test方法的实现
      class_addMethod(object_getClass(self), sel,
               method_getImplementation(method),
               method_getTypeEncoding(method));
      // 返回YES代表有动态添加方法
      return YES;
    }
  return [super resolveClassMethod:sel];
}
#pragma mark 类方法的转换(类方法classTest找不到就转换classOther方法)
+(void)classOther{
   NSLog(@"类方法classTest找不到就转换classOther方法");
}


  • 3.3、消息转发


image.png


解释上面的图:


  • 3.3.1、当动态解析过都找不到一个方法时候就会走消息转发,首先会调用forwardingTargetForSelector(前面可能是 +(void)或者-(void)),如果不返回nil就会走 返回类的 Selector,如果返回nil就走 3.3.2


-(id)forwardingTargetForSelector:(SEL)aSelector{
     if (aSelector == @selector(test)) {
          //return [[People alloc]init];
          return nil;
     }
   return [super forwardingTargetForSelector:aSelector];
}
  • 3.3.2、当forwardingTargetForSelector返回nil或者不写这个方法时候,就会调用下面的方法


#pragma mark 2
/**
  方法签名:返回值类型、参数类型
*/
- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector
{
    if (aSelector == @selector(test)) {
      // 获取其他方法(一),会走 #pragma mark 3
      /*
         Method method = class_getInstanceMethod([People class], @selector(test));
         return [NSMethodSignature signatureWithObjCTypes:method_getTypeEncoding(method)];
       */
      // 下面这一句和上面2句一个意思(二) 会走 #pragma mark 3
      return [NSMethodSignature signatureWithObjCTypes:"v16@0:8"];
      /**
         如果返回nil 就不会再走会走 #pragma mark 3 方法了,会报错返回(doesNotRecognizeSelector)
         -[NSObject(NSObject) doesNotRecognizeSelector:] + 132
       */
      // return nil;
     }
  return [super methodSignatureForSelector:aSelector];
}
#pragma mark 3.上面的方法不返回nil才会走
/**
   NSInvocation封装了一个方法调用,包括:方法调用者、方法名、方法参数
   anInvocation.target 方法调用者
   anInvocation.selector 方法名
   [anInvocation getArgument:NULL atIndex:0]
*/
- (void)forwardInvocation:(NSInvocation *)anInvocation
{
   //    anInvocation.target = [[MJCat alloc] init];
   //    [anInvocation invoke];
   [anInvocation invokeWithTarget:[[People alloc] init]];
 }


  • 当上面的 #pragma mark 2 返回nil的时候,就不会走#pragma mark 3,且报错 doesNotRecognizeSelector


image.png


Runtime消息转发的demo,看Student与People类


目录
相关文章
|
6月前
|
存储 缓存 API
技术笔记:Runtime的相关知识
技术笔记:Runtime的相关知识
47 1
|
7月前
|
Java API
Java包机制及JavaDoc
Java包机制及JavaDoc
|
API
OC底层知识(九) : Runtime(下)
OC底层知识(九) : Runtime(下)
104 0
OC底层知识(九) : Runtime(下)
|
存储 iOS开发 C++
OC 底层知识(一):OC的本质
OC 底层知识(一):OC的本质
251 0
OC 底层知识(一):OC的本质
|
存储 编译器 API
OC底层知识(八) : block
OC底层知识(八) : block
127 0
OC底层知识(八) : block
|
缓存 编译器 iOS开发
[OC Runtime编程指南_翻译]四、消息传递
本章介绍如何将消息表达式转换为objc_msgSend函数调用,以及如何按名称引用方法。然后解释如何利用objc_msgSend,以及如果需要,如何绕过动态绑定。
127 0
[OC Runtime编程指南_翻译]四、消息传递
|
编译器 iOS开发
[OC Runtime编程指南_翻译]八、声明属性
[OC Runtime编程指南_翻译]八、声明属性
178 0
[OC Runtime编程指南_翻译]八、声明属性
|
API
OC底层知识(三): KVC
OC底层知识(三): KVC
189 0
OC底层知识(三): KVC
|
算法 iOS开发
[OC Runtime编程指南_翻译]六、消息转发
[OC Runtime编程指南_翻译]六、消息转发
158 0
[OC Runtime编程指南_翻译]六、消息转发
|
编译器 iOS开发
[OC Runtime编程指南_翻译]七、类型编码
[OC Runtime编程指南_翻译]七、类型编码
111 0
[OC Runtime编程指南_翻译]七、类型编码