WebKit中RefPtr和PassRefPtr的使用

2023-10-28

历史

WebKit中的许多对象是引用计数的(reference counted),采用的模式就是类具有ref和deref成员函数增加和减少引用计数。每个ref调用必须有一个deref与之匹配。当在引用计数值为1的对象上调用deref方法时,对象删除。WebKit中的许多类通过继承RefCounted类模板应用该模式。

时间回溯到2005年,我们发现存在许多由于不正确调用ref和deref而引起的内存泄露,特别是HTML编辑的代码。

我们希望使用智能指针来减少这一问题。但是早期的试验表明智能指针会进行额外的引用计数处理而影响性能。例如,一个函数有一个智能指针的参数并返回该指针指针作为返回值,仅仅传入该参数并返回该值会进行2到4次的增加和减少引用计数值,因为对象从一个智能指针转移到另一个上。因此我们寻找一种方式来让我们使用智能指针同时避免引用计数跳变(churn).

我们从C++标准类模板auto_ptr获得灵感,这些对象实现了一种模型,赋值即是归属关系转移(transfer of ownership),当您从一个auto_ptr赋值到另一个,贡献值变成0.(When you assign from one auto_ptr to another, the donor becomes 0.)

Maciej Stachowiak设计了一对类模板,RefPtr和PassRefPtr,实现这一模式来解决WebKit中恼人的引用计数问题。

原始指针(Raw pointers)

当我们讨论诸如RefPtr类模板之类的智能指针时,通常使用原始指针(raw pointer)指代C++语言中内置的指针类型。下面是经典的使用原始指针(raw pointer)的设置函数:

  1. // example, not preferred style  
  2. class Document {  
  3.     ...  
  4.     Title* m_title;  
  5. }  
  6. Document::Document()  
  7.     : m_title(0)  
  8. {  
  9. }  
  10. Document::~Document()  
  11. {  
  12.     if (m_title)  
  13.         m_title->deref();  
  14. }  
  15. void Document::setTitle(Title* title)  
  16. {  
  17.     if (title)  
  18.         title->ref();  
  19.     if (m_title)  
  20.         m_title->deref();  
  21.     m_title = title;  
  22. }  
  RefPtr

RefPtr是一个简单的智能指针类,它对来值(incoming value)调用ref,对去值(outgoing value)调用deref。RefPtr可用于任何有ref和deref成员函数的对象。下面是使用RefPtr写的设置函数实例:

  1. // example, not preferred style  
  2.    
  3. class Document {  
  4.     ...  
  5.     RefPtr<Title> m_title;  
  6. }  
  7. void Document::setTitle(Title* title)  
  8. {  
  9.     m_title = title;  
  10. }  
  单独使用RefPtr可能会导致引用计数跳变(churn)。

  1. // example, not preferred style; should use RefCounted and adoptRef (see below)  
  2.    
  3. RefPtr<Node> createSpecialNode()  
  4. {  
  5.     RefPtr<Node> a = new Node;  
  6.     a->setSpecial(true);  
  7.     return a;  
  8. }  
  9. RefPtr<Node> b = createSpecialNode();  
 

出于讨论考虑,我们假设节点对象的引用计数起始值为0(稍后更多),当它赋值给a后,引用计数值增加到1。创建返回值后引用计数值又增加到2,接下来a销毁,引用计数值又减少到1。然后创建b后引用计数值增加到2,再下来createSpecialNode返回值销毁后,引用计数值减到1。

(如果编译器实现了返回值优化(return value optimization),就会少一次引用计数增加和减少。)

引用计数跳变(churn)在函数参数和返回值都涉及的情况下更严重,解决的方法就是PassRefPtr。

PassRefPtr

PassRefPtr和RefPtr相似,但存在一处不同,当您拷贝一个PassRefPtr或者赋PassRefPtr值给RefPtr或另一个PassRefPtr时,原来的指针值设置为0,操作不改变引用计数。让我们来看一个新的版本的例子:

  1. // example, not preferred style; should use RefCounted and adoptRef (see below)  
  2. PassRefPtr<Node> createSpecialNode()  
  3. {  
  4.     PassRefPtr<Node> a = new Node;  
  5.     a->setSpecial(true);  
  6.     return a;  
  7. }  
  8. RefPtr<Node> b = createSpecialNode();  
 节点对象的初始引用计数值为0,当它赋值给a后,引用计数值增加到1。当返回值创建后,指针a被赋值为0。当b值创建后,返回值被赋值为0。然而,引用计数保持1不变。

     由于PassRefPtr类型指针赋值给另一个指针后本指针变成为0,所以使用PassRefPtr编程很容易容易导致错误。如下下列代码所示:

  1. // warning, will dereference a null pointer and will not work
  2.  
  3. static RefPtr<Ring> g_oneRingToRuleThemAll;

  4. void finish(PassRefPtr<Ring> ring)
  5. {
  6.     g_oneRingToRuleThemAll = ring;
  7.     ...
  8.     ring->wear();
  9. }

当wear方法被调用时,ring指针已经变为0了。为了避免这种情况发生,建议只是用PassRefPtr类型作为函数参数类型和返回值类型。并且,将参数类型为PassRefPtr的参数赋值给本地的RefPtr类型指针进行操作。如下:

  1. static RefPtr<Ring> g_oneRingToRuleThemAll;

  2. void finish(PassRefPtr<Ring> prpRing)
  3. {
  4.     RefPtr<Ring> ring = prpRing;
  5.     g_oneRingToRuleThemAll = ring;
  6.     ...
  7.     ring->wear();
  8. }

Mixing RefPtr and PassRefPtr

    除了函数传递参数和返回值时使用PassRefPtr类型外,建议使用RefPtr作为计数指针类型。但是,经常会需要RefPtr类型指针象PassRefPtr型指针一样传递指针的所有权。RefPtr类型有一个release成员函数,它通过设置原始的RefPtr指针为0,同时构建一个PassRefPtr类型指针,从而达到不改变指针引用计数的目的。示例代码如下:

  1. // example, not preferred style; should use RefCounted and adoptRef (see below)
  2.  
  3. PassRefPtr<Node> createSpecialNode()
  4. {
  5.     RefPtr<Node> a = new Node;
  6.     a->setCreated(true);
  7.     return a.release();
  8. }

  9. RefPtr<Node> b = createSpecialNode();

上述代码与前面将a定义成PassRefPtr类型的示例代码具有相同的效果。这种用法,不仅保持了PassRefPtr类型指针的高效的特性,同时又避免了PassRefPtr容易出错的问题。

Mixing with raw pointers

      当要获得RefPtr类型中的原始指针时,需要通过get函数来获得原始指针。

下面示例打印原始指针的值:

  1. printNode(stderr, a.get());
然而,RefPtr类型和PassRefPtr类型变量无需显性的调用get的函数就可以直接完成大多数的操作。

示例如下:

  1. RefPtr<Node> a = createSpecialNode();
  2. Node* b = getOrdinaryNode();

  3. // the * operator
  4. *= value;

  5. // the -> operator
  6. a->clear();

  7. // null check in an if statement
  8. if (a)
  9.     log("not empty");

  10. // the ! operator
  11. if (!a)
  12.     log("empty");

  13. // the == and != operators, mixing with raw pointers
  14. if (== b)
  15.     log("equal");
  16. if (!= b)
  17.     log("not equal");

  18. // some type casts
  19. RefPtr<DerivedNode> d = static_pointer_cast<DerivedNode>(a);

通常,RefPtr和PassRefPtr强制实施一个简单的规则,就是成对调用ref和deref,保证编程者不会漏掉对deref的调用。说的ref和deref就不能不说一下RefCounted类,这个类实现了指针的引用计数功能,提供了ref和deref方法。而RefPtr和PassRefPtr只是完成了对ref和deref的自动调用功能,因此这里所说的原始指针(raw pointer)通常是指继承自RefCounted类的指针,换句话说,是具有ref和deref功能的指针。这样的原始指针可以通过adoptRef函数转换成RefPtr类型。

  1. // warning, requires a pointer that already has a ref
  2. RefPtr<Node> node = adoptRef(rawNodePointer);

从RefPtr类型转换成一个原始指针并且不改变引用计数,可以使用PassRefPtr提供的leakRef函数。

  1. // warning, results in a pointer that must get an explicit deref
  2. RefPtr<Node> node = createSpecialNode();
  3. Node* rawNodePointer = node.release().leakRef();

由于leakRef很少被使用,因此只有PassRefPtr类具有这个方法。RefPtr类型的变量使用此方法使,需要先调用release方法,然后才能调用leakRef方法。

RefPtr and new object

在以上讨论的例子中,对象的假设引用计数是从0开始的。这样做的目的为了讲解简单明了,但是RefCounted类的引用计数并不是从0开始的。在构建ReCounted类的构造函数中,引用计数被赋值为1。当deref被调用时,如果引用计数等于1,则删除这个对象。由于ref和deref是成对出现的,为了不遗失调用deref,推荐的用法是在创建对象时应该立即调用adoptRef。在WebCore中,使用create方法代替new方法去创建对象。示例如下:

  1. // preferred style
  2.  
  3. PassRefPtr<Node> Node::create()
  4. {
  5.     return adoptRef(new Node);
  6. }

  7. RefPtr<Node> e = Node::create();

因为adoptRef和PassRefPtr被应用,上述用法是非常高效的。以引用计数为1创建对象,而整个过程没有再操作引用计数。

  1. // preferred style
  2.  
  3. PassRefPtr<Node> createSpecialNode()
  4. {
  5.     RefPtr<Node> a = Node::create();
  6.     a->setCreated(true);
  7.     return a.release();
  8. }

  9. RefPtr<Node> b = createSpecialNode();

对象node通过create函数创建,赋值给a并release,最后赋值给b,整个过程没有触及引用计数。

RefCounted类会进行运行时刻检查,如果创建一个对象,调用ref和deref方法而没有最先调用adoptRef函数,将会声明一个错误。

Guidelines

在WebKit编程中使用RefPtr和PassRefPtr需要遵循如下规则:
Local variables

如果指针的所有权和生命周期可以被保证,一个本地变量可以是一个原始指针。

如果代码需要获得指针的所有权或者确保指针的生命周期,本地变量应该是RefPtr类型。

本地变量永远都不能是PassRefPtr类型。

Data members

如果所有权和生命周期可以被保证,数据成员可以是一个原始指针。

如果类需要获得数据成员指针所有权并保证其生命周期,则数据成员需要定义为RefPtr类型。

数据成员永远都不能是PassRefPtr类型。

Function arguments

如果一个函数不需要获得参数对象的所有权,参数应该使用原始指针。

如果函数需要获得一个对象的所有权,参数应个是一个PassRefPtr类型。这包括大多数的setter函数。除非参数非常简单,在函数的开始PassRefPtr参数应用赋值给一个本地的RefPtr变量。

Function Results

如果一个函数的返回值是一个对象,但是所有权没有被传递,这个返回结果应用是一个原始指针。

如果函数的返回值是一个被创建的对象,或者对象的所有权被传递,返回值应该是PassRefPtr类型。由于本地类型通常是RefPtr,因此在返回时需要调用release方法将RefPtr类型转换成PassRefPtr类型。

New Object

新创建的指针对象应该尽快放在RefPtr类型中,这样可以使智能指针自动完成所有的引用计数。

对于RefCounted类型指针,上述的操作应该由adoptRef函数完成。

最好的用法是使用私有的构造函数和公有的create函数,通过返回一个PassRefPtr指针从而实现对象的创建。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

WebKit中RefPtr和PassRefPtr的使用 的相关文章

  • 将选定的文本扩展为整个单词

    在 IE 中我可以这样做 var rng document selection createRange rng expand word txt rng text 如何在 IE 之外执行相同的操作 使用 getSelection 选择整个单词
  • -不再支持webkit-text-size-adjust,如何将font-size设置为小于12px

    当我将 chrome 更新为28 0 1500 11 开发 m 我找到 webkit text size adjust none 不再支持 我在这里找到了 changlisthttp trac webkit org changeset 14
  • CSS 边框半径不修剪 Webkit 上的图像

    我无法弄清楚为什么在使用 chrome 而不是 firefox 或 ie9 时 border radius 从我的 screen 元素中消失了 我有每个浏览器的所有不同前缀加上标准边框半径 www cenquizqui com 保存图片的上
  • 画布内存使用总量超出最大限制 (Safari 12)

    我们正在研究一个可视化网络应用程序 https affinitymap epfl ch它使用 d3 force 在画布上绘制网络 但现在 iOS 上的浏览 器遇到了问题 在与界面进行几次交互后 进程就会崩溃 据我记得 这不是旧版本 iOS1
  • Chrome webkit 检查器中不断生成“不安全的 JavaScript 尝试使用 URL 访问框架...”错误

    例如 当使用 Facebook API 时 Chrome 或任何其他 webkit 浏览器 会抛出大量 不安全的 JavaScript 尝试使用 URL 访问框架 它不会干扰实际操作 但确实使 javascript 控制台基本无法使用 我想
  • 如何在 OS X 上使用 jsc 运行 JavaScript?

    I got this http trac webkit org wiki JSC当我试图找到某种方法通过终端运行我的 JavaScript 程序时 这run and load提到的命令可以执行外部 JavaScript 文件 帮我看看该怎么
  • 获取插入符位置的元素节点(在 contentEditable 中)

    假设我有一些像这样的 HTML 代码 h1 Some heading text here h1 p Some text here p 现在插入符号 闪烁的光标 在 h1 元素 让我们用这个词来说 heading 如何使用 JavaScrip
  • 如何删除 Cocoa 应用程序中 WebView 存储的 cookie?

    我的 Cocoa 应用程序使用WebView打开使用 cookie 的页面 出于测试目的 我想删除这些 cookie 我怎样才能做到这一点 以编程方式或手动 如果您想以编程方式执行此操作 可以使用NSHTTPCookieStorage ht
  • 我的 CSS 是否正确重置? [复制]

    这个问题在这里已经有答案了 请注意 我只对进行微小的更改感兴趣 而不是应用整个 CSS 重置 我想弄清楚这个特定元素的情况 关于以下内容 一切看起来都很好 我在创建 CSS 时就认出了它 唯一没有意义的是下面的样式规则Basic Style
  • 是否可以像 WebView 一样在 Windows 窗体中嵌入 Gecko 或 Webkit?

    我很想知道是否有 Gecko NET 这样的东西 我的意思是 就像我们可以嵌入一个 WebView 一样 它是任何 Windows 窗体应用程序中 IE7 的 实例 并告诉它navigateto fancy url 我喜欢使用 Firefo
  • JS 库来模拟 Internet Explorer?

    Webkit中有一些JS库可以模拟IE吗 例如 IE8不支持border radius目前 可能在 IE10 上 所以我运行一个 JS 库来检查我是否正在使用border radius然后将其删除以制作类似于IE的视觉 我知道这是一个疯狂的
  • webkit translate3d 问题(peek-thru)

    我正在使用 PhoneGap 构建一个 iOS 应用程序 我使用translate3d CSS 动画来创建 翻转 效果 这对于更简单的元素非常有用 带有前 后 div 的 DIV 可能还有一个或两个额外的跨度 但是当我尝试翻转更大的元素 即
  • 有没有办法让 webkit 的 javascript 正确处理空格?

    事实证明 在处理 XSLT 时 webkit 对于空格的处理似乎并不是特别好 它似乎对换行符 回车符 制表符和空格一视同仁 甚至到了这样的地步 substring after test string 10 将返回string 有什么方法可以
  • HTML 分页

    有没有html分页的开源项目 我正在为 iPhone 开发一个应用程序 我想在 UIWebView 上显示 HTML 文件 并且不希望用户向下滚动以查看屏幕上未显示的剩余内容 我想在第二个 UIWebView 上显示剩余的内容 我怎样才能做
  • 使用 Webkit 的调试版本运行 Safari

    我通过运行以下命令编译了 webkit 的调试版本 工具 脚本 build webkit debug 成功构建后 我尝试通过运行以下命令来使用已编译的 Webkit 调试版本来运行 safari sudo Tools Scripts run
  • 在 Chrome 中使用 webkit-transform 旋转时出现不稳定的文本抗锯齿效果

    我正在使用旋转元素 webkit transform rotate 在 Chrome 14 0 835 2 dev m 中 它对元素内的文本做了一些非常奇怪的事情 它让我想起了在 Photoshop 中使用 平滑 抗锯齿而不是 清晰 旋转文
  • 帮助在 UIWebView 中使用 Javascript 代码

    我想在 webView 中运行以下代码 以便内容可编辑 javascript document body contentEditable true document designMode on void 0 到目前为止 我已经尝试过这个 v
  • Webkit 是否有 CSS3 网格布局的有效实现?

    CSS 网格布局 编辑草案 2011 年 11 月 21 日 http dev w3 org csswg css3 grid align 我正在制作一个原型 该原型将在选定的设备和浏览器上向客户展示 目前我并不担心跨浏览器兼容性 IE10开
  • Javascript:如何持续监控变量值

    如何不断检查变量值 例如 if variable value dosomething 如果我不断循环它或其他东西 这会起作用 但是有没有一种有效的方法可以在变量设置为该值后立即触发它 此解决方案使用已弃用的 API 除了在最旧的浏览器上之外
  • 如何以编程方式确定 HTML 对象可以侦听哪些事件?

    我一直在查看developer mozilla org 和Apple 开发文档上的文档 但我找不到解释是否可以通过编程方式确定特定HTML 标记是否支持给定事件监听器的文档 就像我知道的那样

随机推荐

  • stm32读写SD卡(SPI模式)

    目录 一 SD卡简介 二 源码下载 三 移植条件 1 芯片参数 2 硬件连接 四 驱动代码 1 依赖宏如下 2 驱动代码实现 3 测试代码 4 运行截图 一 SD卡简介 SD卡有SD驱动模式和SPI驱动模式 本例中使用SPI模式驱动SD卡
  • 大公司都有哪些开源项目~~~阿里,百度,腾讯,360,新浪,网易,小米等

    红色字体是现阶段比较火的 奇虎360 https github com Qihoo360 1 MySQL中间层 Atlas Atlas是由 Qihoo 360 Web平台部基础架构团队开发维护的一个基于MySQL协议的数据中间层项目 它在M
  • STM32控制脉搏血氧心率MAX30102模块程序(I2C)

    一 简介 MAX30102是一个集成的脉搏血氧仪和心率监测仪生物传感器的模块 芯片 它集成了一个660nm红光LED 880nm红外光LED 光电检测器 光器件 以及带环境光抑制的低噪声电子电路 可通过软件关断模块 待机电流为零 实现电源始
  • 《海龟投资法则》读书笔记

    本文用于辅助阅读 海龟投资法则 市场 价格形态的类型 核心思想就是把价格波动抽成了波动性和趋势性 怎么让人联想到了光的波粒二象性 根据是否带有波动性和趋势性 可以分成四类价格形态 分别是 平稳 0 0 横向波动 0 1 平稳趋势 1 0 波
  • vue中swiper vue-awesome-swiper的使用方法和爬坑大法

    文章目录 由版本引起的一系列坑 正确的使用姿势 安装 指定版本 组件中使用 总结 由版本引起的一系列坑 时间 2020 07 08 时间 版本很重要 坑的起源 这两天在vue中使用swiper做轮播图 就随手百度了一下 找个帖子开始参考 坑
  • 从原理上理解JDBC的七个步骤,不再死记硬背

    由于平时经常使用持久层框架 早已把最初的JDBC忘到脑后去了 但是JDBC是一切持久层框架的根本 如果想要深入理解框架 就必须要掌握JDBC 最初学JDBC的时候只知道死记硬背那7个步骤 却从来没有理解那些步骤的由来 首先我们要理解JDBC
  • Windwos10最新版开启卓越性能

    老版本开启卓越性能的方法不管用了 1 首先右击开始菜单 打开power shell 管理员 将下面代码复制粘贴进去按回车 powercfg duplicatescheme e9a42b02 d5df 448d aa00 03f14749eb
  • 【多模态】UNIMO

    一 unimo 1 优点 训练数据包含文本 图像 图文对数据训练 不局限于图文对 2 策略和模型 1 文本改写 Text Rewriting 为了增强图文在多个粒度上的语义对齐能力 论文将图像的文本描述从句子级 短语级和词汇级别三个粒度进行
  • 【PCIE体系结构十】链路两端的参考时钟有频偏怎么办?

    个人主页 highman110 作者简介 一名硬件工程师 持续学习 不断记录 保持思考 输出干货内容 参考书籍 PCI EXPRESS系统体系结构标准教材 Mindshare PCIE规范中有描述PCIE支持公共参考时钟架构和独立参考时钟架
  • 合并排序

    与很多有用的算法类似 合并排序基于这样一个技巧 将 2 个大小为 N 2 的已排序序列合并为一个 N 元素已排序序列仅需要 N 次操作 这个方法叫做合并 我们用个简单的例子来看看这是什么意思 通过此图你可以看到 在 2 个 4元素序列里你只
  • STM32定时器——编码器模式采集EC11旋转编码器

    目录 一 EC11旋转编码器介绍 二 定时器编码器模式介绍 三 编码器初始化代码介绍 四 旋转EC11向串口发送数据 一 EC11旋转编码器介绍 EC11旋转编码器广泛用于车载DVD 车载导航 汽车影音上 常被人称为车载编码器 编码器主要用
  • Vue2中使用自定义指令

    在vue中 除了vue提供了一套内置的指令外 Vue 也允许注册自定义指令 用来配置重写某一项数据的方法 声明自定义指令 分为全局注册自定义指令 以及局部注册自定义指令 全局注册 为 Vue directive 接收两个参数 第一个参数为字
  • HADOOP调优与运维问题汇总

    一 Hadoop调优 1 调整操作系统打开文件描述符的上限 通过命令 ulimit a 可以看到所有系统资源参数 这里面需要重点设置的是 open files 和 max user processes 其它可以酌情设置 要永久设置资源参数
  • python 中 np.sum()函数 通俗易懂理解!

    查看我的另一篇博客更清晰 https blog csdn net goodshot article details 79709884 这一篇博客保证是我写的最清楚 最容易理解的博客 众所周知 sum不传参的时候 是所有元素的总和 这里就不说
  • AJAX的使用方式及教程

    1 概念 ASynchronous JavaScript And XML 异步的JavaScript 和 XML 1 异步和同步 客户端和服务器端相互通信的基础上 客户端必须等待服务器端的响应 在等待的期间客户端不能做其他操作 客户端不需要
  • 数据挖掘一些面试题总结(Data Mining)

    Data Mining试题 2011Alibaba数据分析师 实习 试题解析 一 异常值是指什么 请列举1种识别连续型变量异常值的方法 异常值 Outlier 是指样本中的个别值 其数值明显偏离所属样本的其余观测值 在数理统计里一般是指一组
  • Fastjson

    1 介绍 Fastjson是阿里巴巴的开源JSON解析库 它可以解析JSON格式的字符串 支持将Java Bean序列化为JSON字符串 也可以从JSON字符串反序列化到JavaBean 具有执行效率高的特点 应用范围广泛 Fastjson
  • (基础)选择器的语法

    1 什么是选择器 CSS选择器是CSS规则的第一部分 每个CSS规则都以一个选择器或一组选择器为开始 CSS 选择器即用于 查找 或选取 要设置样式的 HTML 元素的模式 选择器可以分为基础选择器 复合选择器 2 基本选择器 1 通配 通
  • SpringBoot应用程序的打包和部署 [学习笔记]

    传统Web应用打包部署时 通常会用War包的形式 然后将War包部署到Tomcat等服务器中 而Spring Boot应用是嵌入式Servlet容器 默认以Jar包形式进行打包部署的 而如果想要使用传统的War包形式打包部署 需要进行配置
  • WebKit中RefPtr和PassRefPtr的使用

    历史 WebKit中的许多对象是引用计数的 reference counted 采用的模式就是类具有ref和deref成员函数增加和减少引用计数 每个ref调用必须有一个deref与之匹配 当在引用计数值为1的对象上调用deref方法时 对