从AOP框架学习iOS Runtime

最近在一个iOS项目中添加了一个AOP框架,并且根据项目需求做了一些重构,重构的过程中对AOP的实现方式也做了下学习和分析,感觉还是很有趣的,下面就给大家分享一下个人所得。
     在正式讲解iOS中AOP实现原理之前,有些准备知识需要讲一下
  

   准备知识

     

   准备知识一:Method,SEL,IMP概念

    SEL

     先看一下SEL的概念,Objective-C在编译时,会依据每一个方法的名字、参数序列,生成一个唯一的整型标识(Int类型的地址),这个标识就是SEL。
     SEL也是@selector的类型,用来表示OC运行时的方法的名字。来看一下OC中的定义
     
      本质上,SEL只是一个指向方法的指针(准确的说,只是一个根据方法名hash化了的KEY值,能唯一代表一个方法),它的存在只是为了加快方法的查询速度。这个查找过程我们将在下面说明。
      我们可以在运行时添加新的selector,也可以在运行时获取已存在的selector。

    IMP

      实际上是一个函数指针,指向方法实现的首地址,定义如下:
      
      关于IMP的几点说明:

  • 使用当前CPU架构实现的标准的C调用约定
  • 第一个参数是指向self的指针(如果是实例方法,则是类实例的内存地址;如果是类方法,则是指向元类的指针)
  • 第二个参数是方法选择器(selector),
  • 从第三个参数开始是方法的实际参数列表。

      通过取得IMP,我们可以跳过Runtime的消息传递机制,直接执行IMP指向的函数实现,这样省去了Runtime消息传递过程中所做的一系列查找操作,会比直接向对象发送消息高效一些,当然必须说明的是,这种方式只适用于极特殊的优化场景,如效率敏感的场景下大量循环的调用某方法。

    Method

      直接上定义:
      
      Method = SEL + IMP + method_types,相当于在SEL和IMP之间建立了一个映射

   准备知识二:iOS方法调用流程

      方法调用的核心是objc_msgSend方法:
             objc_msgSend(receiver, selector, arg1,arg2,…)
       具体的过程如下:

  1. 先找到selector 对应的方法实现(IMP),因为同一个方法可能在不同的类中有不同的实现,所以需要receiver的类来找到确切的IMP

      • IMP class_getMethodImplementation(Class class, SEL selector)
      • 如同其文档所说:The function pointer returned may be a function internal to the runtime instead of an actual method implementation. For example, if instances of the class do not respond to the selector, the function pointer returned will be part of the runtime's message forwarding machinery.
      • 具体来说,当找不到IMP的时候,方法返回一个 _objc_msgForward 对象,用来标记需要转入消息转发流程,我们现在用的AOP框架也是利用了这个机制来人为的制造找不到IMP的假象来触发消息转发的流程

    • 如果实在对_objc_msgFroward的内部实现感兴趣,只能看看源码了,只不过都是汇编实现的....感兴趣的同学可以想想为什么是用汇编来实现
    • 这里有个源码的镜像https://github.com/opensource-apple ,如果翻墙费劲的话
  2. 根据查找结果
    • 找到了IMP,调用找到的IMP,传入参数
    • 没找到IMP,转入消息转发流程
  3. 将IMP的返回值作为自己的返回值

      补充说明一下IMP的查找过程,消息传递的关键在于objc_class结构体中的以下几个东西:

  • Class *isa
  • Class *super_class
  • objc_method_list **methodLists
  • objc_cache *cache

       当消息发送给一个对象时,objc_msgSend通过对象的isa获取到类的结构体,然后在cache和methodLists中查找,如果没找到就找其父类,以此类推知道找到NSObject类,如果还没找到,就走消息转发流程。

   准备知识三:iOS方法转发流程

      从上文中我们看到当obj无法查找到 IMP时,会返回一个特定的IMP _objc_msgForward , 然后会进入消息转发流程,具体流程如下:

  1. 动态方法解析

    • resolveInstanceMethod:解析实例方法 
    • resolveClassMethod:解析类方法
    • 通过class_addMethod的方式将缺少的selector动态创建出来,前提是有提前实现好的IMP(method_types一致)
    • 这种方案更多的是位@dynamic属性准备的
  2. 备用接受者(AOP中有使用)
    • 如果上一步没有处理,runtime会调用以下方法

      • -(id)forwardingTargetForSelector:(SEL)aSelector
    • 如果该方法返回非nil的对象,则使用该对象作为新的消息接收者
      • 不能返回self,会出现无限循环
      • 如果不知道该返回什么,应该使用[super forwardingTargetForSelector:aSelector]
    • 这种方法属于单纯的转发,无法对消息的参数和返回值进行处理
  3. 完整转发(AOP中有使用)
    • - (void)forwardInvocation:(NSInvocation *)anInvocation
    • 对象需要创建一个NSInvocation对象,把消息调用的全部细节封装进去,包括selector, target, arguments 等参数,还能够对返回结果进行处理
    • 为了使用完整转发,需要重写以下方法
      • -(NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector,如果2中return nil,执行methodSignatureForSelector:
      • 因为消息转发机制为了创建NSInvocation需要使用这个方法吗获取信息,重写它为了提供合适的方法签名

AOP核心逻辑解析

        到了有意思的戏肉部分,打算用流程图的方式解析一下核心的两个流程:拦截器(intercepter)注册流程和拦截器(intercepter)执行流程。

   拦截器(intercepter)注册流程


说明:(图中m:代表Method,ClassA是AOP的目标类,X是AOP的目标方法,AOPAspect是AOP处理类-单例)

  1. 将原始的X的IMP拿出来,以特定的命名规则动态加入AOPAspect
  2. 将X的IMP替换为_objc_msgForward,用这种比较tricky的方式来触发消息转发流程
  3. 将ClassA中原有的forwardingTargetForSelector:的IMP以特定的命名规则存入AOPAspect
  4. 将ClassA的forwardingTargetForSelector:的IMP用AOPApect中的baseClassForwardingTargetForSelector替换,其中的具体逻辑见下面的代码
  5. 后边的就是将拦截器的信息和block存入到AOPAspect中,细节就不讲了,有兴趣的同学可以到github上看看原始版

   拦截器(intercepter)执行流程

说明:(图中m:代表Method,ClassA是AOP的目标类,X是AOP的目标方法,AOPAspect是AOP处理类-单例,IMP是方法对应的实现)

  1. 开始调用,objc_msgSend开始查找SEL为X的IMP,查到结果为_objc_msgForward,触发ClassA的转发流程
  2. ClassA中转发流程调用forwardingTargetForSelector:,实际会调用替换上去的baseClassForwardingTargetForSelector:的IMP,这个IMP正常情况下会返回AOPAspect的单例作为target(代码见上文图)
  3. 接下来开始在AOPAspect的单例中执行转发流程,经过一系列的3.1-3.5的跳转查找,最终会触发转发流程的forwardingInvocation方法
  4. 在forwardingInvocation中触发一系列的interceptors的执行(包括原始的X的IMP),代码见下图
  5. 后边的interceptor的执行细节也略过了,有兴趣的同学可以到github上看看原始版

总结

       讲到这里iOS AOP框架中的核心的思路和流程就算是讲完了,其实这些之外其他的代码有兴趣的同学也可以看看,也欢迎大家拍砖~

该文章来自于阿里巴巴技术协会(ATA)

作者:林熠

时间: 2024-09-28 12:30:06

从AOP框架学习iOS Runtime的相关文章

Emit学习-实战篇-实现一个简单的AOP框架(一)

周末两天窝在家里,使用Emit做了一个非常简单的AOP框架,当做是这几周学习Emit后的一个实践.东西出来了,自然要和大家分享一下,虽然框架做的比较粗糙.简单,但是也已经能够看到一点AOP的雏形了,用来自己无聊玩玩还是可以的,当然要用到产品中去肯定还需要长期的完善啦. 说起AOP相信园子里很多高手都研究过,园子里好像也有自己的开源AOP项目,不过我时间有限没有细找,同时也发现园子研究AOP理论方面的文章很多,但是好像并没有完整的实现一个简单AOP框架的例子(当然我只是简单的找了一下,如有遗漏恳请

iOS中 HeathKit框架学习 步数统计等 韩俊强的博客

每日更新关注:http://weibo.com/hanjunqiang  新浪微博!iOS开发者交流QQ群: 446310206 HeathKit框架学习 本文结构 简介 用户数据安全及隐私 HeathKit框架 HeathKit使用 总结 简介 HeathKit是Apple公司在推出iOS 8 系统时一块推出的关于健康信息的框架.如果iPhone手机系统升级到iOS8之后就会发现多了一个健康-app,这就是Apple提供的一个记录用户健康信息的app,可以用它来分享健康和健身数据.还可以指定数

J2EE中使用Spring AOP框架和EJB组件

j2ee 快速发展的开发人员社区.对各种后端技术(包括JMS.JTA.JDO.Hibernate.iBATIS等等)的支持,以及(更为重要的)非侵入性的轻量级IoC容器和内置的AOP运行时,这些因素使得Spring Framework对于J2EE应用程序开发十分具有吸引力.Spring托管的组件(POJO)可以与EJB共存,并允许使用AOP方法来处理企业应用程序中的横切方面--从监控和审计.缓存及应用程序级的安全性开始,直到处理特定于应用程序的业务需求. 本文将向您介绍Spring的AOP框架在

[翻译] 学习iOS开发的建议:如何从菜鸟到专家

[文章原地址] http://mobile.tutsplus.com/tutorials/iphone/ios-quick-tip-from-novice-to-expert/ 翻译有误之处请勿见笑,本人将在文章的部分地方添加注释,并根据需求增减文章内容,在此对原作者辛勤劳作表示感谢  iOS Quick Tip: From Novice to Expert Bart Jacobs on Jul 29th 2013 with 5 comments Even though it's possibl

Dexposed:Android平台免Root无侵入AOP框架

本文来自阿里巴巴技术协会(ATA) 本文首发于 http://www.infoq.com/cn/news/2015/07/dexposed 近日,阿里巴巴无线事业部推出首个重量级Android开源项目,名为Dexposed,是一个Android平台下的无侵入运行期AOP框架.旨在解决像性能监控.在线热补丁等移动开发常见难题,典型使用场景为: AOP编程 插桩 (如测试.性能监控等) 在线热补丁 SDK hooking以提供更好的开发体验 它基于ROOT社区著名开源项目Xposed改造剥离了ROO

Enterprise Library深入解析与灵活应用(6):自己动手创建迷你版AOP框架

基于Enterprise Library PIAB的AOP框架已经在公司项目开发中得到广泛的使用,但是最近同事维护一个老的项目,使用到了Enterprise Library 2,所以PIAB是在Enterprise Library 3.0中推出的,所以不同直接使用.为了解决这个问题,我写了一个通过方法劫持(Method Interception)的原理,写了一个简易版的AOP框架.(如果对PIAB不是很了解的读者,可以参阅我的文章MS Enterprise Library Policy Inje

Spring事务处理及其AOP框架的内幕

Spring框架中成功吸引人的一点就是容器事务的管理,提供了一个轻量级的容器事务处理,针对的对象是普通的java类,使用Spring事务管理的话,你可以按照自己的业务把一些相关的方法纳入其事务管理里面,这就避免了程序员在处理事务的过程中繁琐的工作.同时这些也是ejb2.X规范里面吸引人的一点,这在spring里面都很好的提供.虽然在跨容器的事务管理,spring里面并没有提供,但是对于一般的web程序来说,也不需要仅仅为了那些功能而不得不使用ejb.不过,最近jboss的嵌入式的ejb容器也可以

怎么入手学习ios开发呢

问题描述 怎么入手学习ios开发呢 本人做android有段时间了,想转ios该如何入手呢,望多提建议.... 从oc知识方面 解决方案 其实语言都差不多,你这有安卓基础更容易了,不过也有弊端,用习惯了JAVA,再用OC的话肯定感觉别扭, 我这个视频下载地儿,一个是快速入门的,一个是零基础的,你看看哪个需要;http://ios.itcast.cn/ios/video.shtml?131129zy&csdn

SSH框架学习总结

SSH框架学习总结 最终版权:JDram314 如转载请贴出出处! 本来对SSH框架的学习可以早在去年, 但是一直在给老师弄他的科研部分, 所以一直拖到最近才算是学完了.乘现在有空总结一下,方便以后复习.          一.Struts         在没有学习SSH框架前,我们一般采用Jsp+java bean+servlet开发,这里就是MVC架构.而Struts 其实就是替代了 Servlet,我们知道Servlet在一般的开发中做控制页面跳转,同时调用系统的 业务逻辑层.现在想想S