使用objc runtime实现iOS闭环的懒加载功能

使用objc runtime实现懒加载

地址:AutoPropertyCocoa

懒加载形式如下

- (id)lazyloadProperty{

  if(_lazyloadProperty == nil){

    _lazyloadProperty = [XClass ...];
  }
  return _lazyloadProperty;
}

一般使用宏定义可以轻松完成。但是没有一致性,移植差。

利用objc runtime的动态性实现懒加载可以实现即可增加又可删除功能,也可以避免污染类型。该三方弥补了目前没有闭环实现懒加载三方的空缺。

主要流程:

  1. 实例或者类的懒加载
  2. 如果是实例对象则钩住并修改类型将其子类化
  3. 对该类型进行method swizzling
  4. 如果现在进行解绑,则判断是否是自己实现的方法.如果是自己实现的方法->5,否则->6
  5. 调用method swizzling还原
  6. 删除这个类型的这个方法

难点:

自己实现method swizzling

  1. 重新实现objc1时代的方法class_removeMethods
  2. 钩住运行时中的runtimelock,实现修改类型数据时的安全性

我们再实现method swizzling时的两个API

OBJC_EXPORT IMP _Nullable
class_replaceMethod(Class _Nullable cls, SEL _Nonnull name, IMP _Nonnull imp,
          const char * _Nullable types)
  OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

OBJC_EXPORT void
method_exchangeImplementations(Method _Nonnull m1, Method _Nonnull m2)
  OBJC_AVAILABLE(10.5, 2.0, 9.0, 1.0, 2.0);

不管使用哪种,如果这个类型没有实现该方法而是父类实现的话,就需要动态增加一个方法。动态增加的方法在Objc1时代,是可以通过下列方法删除的:

OBJC_EXPORT void
class_removeMethods(Class _Nullable, struct objc_method_list * _Nonnull)
  OBJC2_UNAVAILABLE;

Objc2时代之后runtime被重写后没有该方法了,并且新的runtime的类结构看起来就没打算让开发者删除方法,所以这里将过程记下。

首先看类读写器的结构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;//删除这里的一个方法
  property_array_t properties;
  protocol_array_t protocols;

  Class firstSubclass;
  Class nextSiblingClass;

  char *demangledName;

#if SUPPORT_INDEXED_ISA
  uint32_t index;
#endif
};

method_array_t继承于list_array_tt<method_t, method_list_t>,它是数组结构。存储的内容是method_list_t.

method_list_t又继承于entsize_list_tt<method_t, method_list_t, 0x3>,他也是数组结构。

整个method_array_t结构是二维数组。每次删掉一个method_t需要用新method_list_t替换原对象。

然后是线程安全的问题,需要获取到苹果在操作类型的时候使用的读写锁(pthread_rw_lock_t runtimelock)。没有这把锁任何对runtime的修改都是不可靠的。

最终采取的方式是:劫持暴露了符号的系统函数然后阻塞线程

劫持系统C函数使用的是脸书的鱼钩,这个钩子在macOS其实也是可以正常工作的。

剩下的就是寻找合适的函数了,这函数要满足两个条件:

  1. 该函数在符号表中存在
  2. 函数内部在lock runtimelock之后存在满足条件1的第二个函数

找了半天发现最合适的只有objc_allocateProtocol()了,objc_allocateProtocol内部会调用calloc(),所以第二个被劫持函数就是calloc。为了减小calloc的开销,需要稍微做一些工作。

  1. 对每次调用进行比较线程ID的操作显然比暴力阻塞线程好。
  2. 减小劫持后的calloc的调用栈。

总结

以上所述是小编给大家介绍的使用objc runtime实现iOS闭环的懒加载功能,希望对大家有所帮助,如果大家有任何疑问欢迎给我留言,小编会及时回复大家的!

(0)

相关推荐

  • 解析iOS应用的UI开发中懒加载和xib的简单使用方法

    懒加载 1.懒加载基本 懒加载--也称为延迟加载,即在需要的时候才加载(效率低,占用内存小).所谓懒加载,写的是其get方法. 注意:如果是懒加载的话则一定要注意先判断是否已经有了,如果没有那么再去进行实例化 2.使用懒加载的好处: (1)不必将创建对象的代码全部写在viewDidLoad方法中,代码的可读性更强 (2)每个控件的getter方法中分别负责各自的实例化处理,代码彼此之间的独立性强,松耦合 3.代码示例 复制代码 代码如下: // //  YYViewController.m //

  • iOS 懒加载的使用实例代码

    写在前面 昨天晚上写的那个代码在配置数组数据的时候突然想到加载数据的时候可以不用在viewDidLoad中加载,可以什么时候用什么时候加载,这种技术就是这篇文章的懒加载方法.然后记录一下.内容如下: 什么是懒加载 懒加载又称为延迟加载,它是指系统不会在初始化是就加载某个对象,而是在第一次调用(使用 get 方法)时才加载这个对象到内存,简单点儿说就是当对象被系统或者开发者需要用到的时候再去加载.它的实现方法实质上就是覆写该对象的 get 方法,并将该对象在初始化时需要实现的代码在 get 方法中

  • 使用objc runtime实现iOS闭环的懒加载功能

    使用objc runtime实现懒加载 地址:AutoPropertyCocoa 懒加载形式如下 - (id)lazyloadProperty{ if(_lazyloadProperty == nil){ _lazyloadProperty = [XClass ...]; } return _lazyloadProperty; } 一般使用宏定义可以轻松完成.但是没有一致性,移植差. 利用objc runtime的动态性实现懒加载可以实现即可增加又可删除功能,也可以避免污染类型.该三方弥补了目前

  • vue 实现图片懒加载功能

    一个页面图片比较多的时候,需要对界面的图片进行懒加载处理,今天遇到了,做个懒加载的笔记. 1,需要安装vue的懒加载插件. npm install vue-lazyload --save-dev 2,需要在main.js里面进行引用. import VueLazyload from "vue-lazyload"; Vue.use(VueLazyload); 或者自定义 Vue.use(VueLazyload, { preLoad: 1.3, error: 'dist/error.png

  • mybatis源码解读之executor包懒加载功能 

    ProxyFactory是创建代理类的工厂接口,其中的setProperties方法用来对工厂进行属性设置,但是mybatis内置的两个实现类都没有实现该接口,所以不支持属性设置.createProxy方法用来创建一个代理对象 public interface ProxyFactory {   // 设置工厂属性   default void setProperties(Properties properties) {   }   // 创建代理对象   Object createProxy(O

  • 基于angular6.0实现的一个组件懒加载功能示例

    我们常常会遇到这样一个问题,当我们使用一个第三方控件库的时候,我们只用到了其中 1 个或某几个组件,会连带一大堆无用的东西,造成体积臃肿不堪.又或者首页用到的组件较多,首页加载速度缓慢,这个时候,我们或许需要加载用户可视范围内用到的组件,随着用户的浏览下拉,我们再去加载这些组件,渐进式加载,渐进式体验,这个时候你或许就用到了本工具所实现的功能.或者一个页面的某些不重要区域,比如第三方广告又或者不重要的元素,可以采用懒加载懒渲染,降低用户首屏等待时间.一切都在用户不知不觉中进行.大大增加用户体验,

  • vue2+elementUI的el-tree的懒加载功能

    lazy 属性为true 时生效lazy ----> 是否懒加载子节点,需与 load 方法结合使用 isLeaf可以提前告知 Tree 某个节点是否为叶子节点,从而避免在叶子节点前渲染下拉按钮. HTML部分 <el-tree class="filter-tree" ref="tree" accordion :data="leftData" @node-click="handleNodeClick" node-k

  • Android仿今日头条多个fragment懒加载的实现

    前言 最近有时间,所以我又双叒叕推新一篇文章了,fragment懒加载实现虽然是个小模块,但做过的人都有体会,通常并不会轻易就成功了的,让你辗转反侧,彻夜难眠,绵绵无绝期.我就按照今日头条的样式做了一个懒加载功能.文章到一半会解释大家可能遇到的不加载数据了的坑,先不剧透. Fragment的生命周期回顾 github代码直通车 (本地下载) 这里是今日头条效果: 自制效果,有图有真相: 实现思路: 使用Fragment类自带方法setUserVisibleHint()判断当前fragment是否

  • vue3+typescript实现图片懒加载插件

    github项目地址: github.com/murongg/vue- 求star 与 issues 我文采不好,可能写的文章不咋样,有什么问题可以在留言区评论,我会尽力解答 本项目已经发布到npm 安装: $ npm i vue3-lazyload # or $ yarn add vue3-lazyload 需求分析 支持自定义 loading 图片,图片加载状态时使用此图片 支持自定义 error 图片,图片加载失败后使用此图片 支持 lifecycle hooks,类似于 vue 的生命周

  • MyBatis深入解读懒加载的实现

    懒加载 ,也称为嵌套查询 需要查询关联信息时,使用 Mybatis 懒加载特性可有效的减少数据库压力, 首次查询只查询主表信息,关联表的信息在用户获取时再加载. Mybatis 一对一关联的 association 和一对多的 collection 可以实现懒加载.懒加载时要 使用resultMap,不能使用 resultType . 这里我们以员工表和部门表为例 通过deptId 与 部门表 id 关联 我们这里首先需要开启一个设置 <settings> <!--指定哪些方法去触发延迟

随机推荐