本文主要是介绍Webkit RefPtr and PassRefPtr Basic,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
RefPtr and PassRefPtr Basics
历史:
在webkit当中很多对象是使用引用计数方式。这种方式的实现是通过每个类当中拥有ref和deref成员函数用来递增和减小引用计数。每一个ref方法调用和deref相对应。当引用计数变为0时,这个对象就会被delete。在webkit当中的很多类实现这种模式的方法是继承RefCounted类模板。
回到2005年,我们发现有很多内存泄露问题,尤其是在HTML编辑代码,问题的原因是ref 和deref 的使用混乱。
我们想通过智能指针来缓解这个问题。然而,早期的经验显示,智能指针导致额外的开销操作引用计数,这会影响性能。举个例子:有个函数需要一个智能指针参数,并且将相同的智能指针返回,仅仅在传递智能指针和返回这个智能指针,从一个智能指针到另外一个智能指针,会增加和减小引用计数2-4次;因此我们需要找一个折中的方法来解决这个问题。
启发式的方案来自C++标准模板类auto_ptr. 这个类的对象实现一个模型,当赋值时交换对象拥有权。当你赋值一个auto_ptr对象到另外一个时,赋值者会变为0;
Maciej Stachowiak设计了一对类模板,RefPtr 和 PassRefPtr,这个实现组合嵌入到WebCore的引用计数。
Raw points
当讨论像RefPtr类模板智能指针时,我们使用 term raw pointer 来引用 C++语言编译指针类型。下面是setter function 的教程,通过raw point实现
// example, not preferred style
class Document {
...
Title* m_title;
}
Document::Document()
: m_title(0)
{
}
Document::~Document()
{
if (m_title)
m_title->deref();
}
void Document::setTitle(Title* title)
{
if (title)
title->ref();
if (m_title)
m_title->deref();
m_title = title;
}
RefPtr:
RefPtr是一个简单的智能指针类,当incoming value时调用ref方法,当outcoming value时带调用deref方法。RefPtr通过ref,deref方法,作用于任何对象。例子如下:
// example, not preferred style
class Document {
...
RefPtr<Title> m_title;
}
void Document::setTitle(Title* title)
{
m_title = title;
}
单独使用RefPtr会导致引用计数错乱。
// example, not preferred style; should use RefCounted and adoptRef (see below)
RefPtr<Node> createSpecialNode()
{
RefPtr<Node> a = new Node;
a->setSpecial(true);
return a;
}
RefPtr<Node> b = createSpecialNode();
作为讨论的目的,我们假设节点对象最开始的引用计数为0.当被赋值给a 对象时,引用计数递增为1,当创建完返回值之后引用计数被递增为2,当a被销毁的时候,引用计数变为1.当创建对象b时引用计数递增为2。当createSpecialNode执行完毕,引用计数递减为1.
如果编译器优化返回值,那么这里会少一次递增和递减的调用。这种引用计数的开销解决方案是PassRefPtr。
PassRefPtr像是RefPtr的另外一种实现。当你copy一个PassRefPtr 或者 赋值PassRefPtr对象到RefPtr或者另外一个PassRefPtr,原始的指针值会被设置为0;这个操作完成不需要任何引用计数的调用();下面是重新实现createSpecialNode方法的例子:
/ example, not preferred style; should use RefCounted and adoptRef (see below)
PassRefPtr<Node> createSpecialNode()
{
PassRefPtr<Node> a = new Node;
a->setSpecial(true);
return a;
}
RefPtr<Node> b = createSpecialNode();
同样 node 对象的开始引用计数为0,当赋值给对象a时,引用计数递增为1,当PassRefPtr(return value)对象被创建时a被赋值为0,当对象b创建时return value 被设置为0;然而,学习safari team的方式,在我们开始使用PassRefPtr,规则是当赋值给另外一个变量指针会被设置为0,这个操作可能容易导致误解。
// warning, will dereference a null pointer and will not work
static RefPtr<Ring> g_oneRingToRuleThemAll;
void finish(PassRefPtr<Ring> ring)
{
g_oneRingToRuleThemAll = ring;
...
ring->wear();//this time ring is NULL
}
当wear被调用时,ring已经为空,为了避免这种情况,我们建议PassRefPtr仅在函数传递参数和返回值,拷贝参数到RefPtr 本地变量使用。
static RefPtr<Ring> g_oneRingToRuleThemAll;
void finish(PassRefPtr<Ring> prpRing)
{
RefPtr<Ring> ring = prpRing;
g_oneRingToRuleThemAll = ring;
...
ring->wear();
}
Mixing RefPtr and PassRefPtr
自从我们建议在所有的case使用RefPtr除了在 传参数 或者返回值。当你拥有RefPtr且想要同PassRefPtr交换所有权这需要几次ref,deref调用。RefPtr 有一个成员函数叫release可以trick。Release方法设置原始的RefPtr为0, 并且构造一个PassRefPtr 而不需要改变引用计数。
// example, not preferred style; should use RefCounted and adoptRef (see below)
PassRefPtr<Node> createSpecialNode()
{
RefPtr<Node> a = new Node;
a->setCreated(true);
return a.release();
}
RefPtr<Node> b = createSpecialNode();
这个方法可以保持PassRefPtr的效率,减少case 欺骗语义所带来的问题。
Mixing with raw pointers(和raw point混合使用)
当使用一个RefPtr来调用一个带有raw Pointf参数方法,使用get函数
printNode(stderr, a.get());
然而,很多操作可以直接通过RefPtr,PassRefPtr来完成,而不需要重新排序到显示的get调用。
RefPtr<Node> a = createSpecialNode();
Node* b = getOrdinaryNode();
// the * operator
*a = value;
// the -> operator
a->clear();
// null check in an if statement
if (a)
log("not empty");
// the ! operator
if (!a)
log("empty");
// the == and != operators, mixing with raw pointers
if (a == b)
log("equal");
if (a != b)
log("not equal");
// some type casts
RefPtr<DerivedNode> d = static_pointer_cast<DerivedNode>(a);
一般来说,RefPtr,PassRefPtr,强制一个简单的规则,他们总是平衡在rel,deref调用,保证程序员不会错过deref调用。但是在这个case当中我们有一个指针类型,已经拥有引用计数,而且想交换拥有权,adoptRef函数可以使用。
// warning, requires a pointer that already has a ref
RefPtr<Node> node = adoptRef(rawNodePointer);
如果需要交换一个RefPtr到一个指针类型,而不需要更改引用计数,则可以通过调用PassRefPtr的leadRef函数
// warning, results in a pointer that must get an explicit deref
RefPtr<Node> node = createSpecialNode();
Node* rawNodePointer = node.release().leakRef();
RefPtr and new objects
在这个例子当中我们一直假设对象刚开始的引用计数为0,实际上创建一个对象其引用计数是1。所以最好的编码习惯是将这样的对象带进到一个RefPtr当中,来确保不可能忘记调用deref。换句话说,任何人new一个RefPtr对象时,应该立即调用adoptRef,在webcore当中我们使用create方法直接代替new操作符。
// preferred style
PassRefPtr<Node> Node::create()
{
return adoptRef(new Node);
}
RefPtr<Node> e = Node::create();
由于我们实现了adoptRef和passRef方法,这是一个有效的习惯。对象开始的引用计数为1,而且没有引用计数改变的操作发生。
// preferred style
PassRefPtr<Node> createSpecialNode()
{
RefPtr<Node> a = Node::create();
a->setCreated(true);
return a.release();
}
RefPtr<Node> b = createSpecialNode();
Node对象同过create被放置到一个PassRefPtr对象当中,然后传递给a对象,然后调用release方法返回给b对象,一系列操作都不需要触及引用计数改变。
RefCounted 实现一个运行时的检查:如果我们创建一个对象并调用ref,deref方法之前,没有调用adoptRef将会收到assert failure.
使用指南(RefPtr,PassRefPtr在webcore中的使用)
Local variable(局部变量):
1.如果拥有者和生命周期有保证,局部变量可以是指针类型(raw point);
2.如果代码需要hold拥有者或者保证生命周期,局部变量需要是RefPtr;
3.局部变量不能是PassRefPtr;
Data Member(成员数据):
1.如果拥有者和生命周期有保证,局部变量可以是指针类型(raw point);
2.如果代码需要hold拥有者或者保证生命周期,局部变量需要是RefPtr;
3.局部变量不能是PassRefPtr;
Function Arguments(函数参数)
1.如果一个函数没有把持对象,参数可以为raw point指针类型。
2.若果一个函数持有对象,参数必须为PassRefPtr,这个包含绝大多数的setter function,除非参数非常简单,参数应该在函数的开始处转换为RefPtr。
Function Results
1.如果函数返回值是一个对象,但是拥有者没有被转换,返回值必须是raw point(指针类型),这种case包含绝大多数的getter function。
2.如果函数返回值是一个new object 或者 所有权因为各种原因被转换了,返回值结果需要时PassRefPtr类型,常见的方法是在返回的时候调用release 将RefPtr转换为PassRefPtr;
New Object
1.new object 应该尽快被放置到RefPtr中,放置完毕之后可以让智能指针自动进行引用计数。
2.对于RefCounted objects,上面这种情况需要使用adoptRef 函数。
3.最好的编码习惯是private constructor 然后 public 一个create方法并返回 PassRefPtr;
Improving this document
1.为了保险起见,用一个局部RefPtr来保持活跃的对象。
2.TreeShared 编程的陷阱.
3.我们渴望去消除TreeShared,采用m_firstChild和m_next 作为ListRefPtr 来替换消除这个问题。
4.实现DOM 及javascipt 和 Objective-C DOM binding我们在垃圾回收的时引用计数混乱。
5.和boost库中的sharedPtr相比较我们的引用计数实现方式。
6.对于OwnPtr类模板,我们需要知道他是怎么用PassOwnptr 和 adoptPtr;
7.The OwnArrayPtr class template, and PassOwnArrayPtr
8.The RetainPtr class template, and the lack of a PassRetainPtr.
9.The ListRefPtr class template.
Translate by miechal zhao
这篇关于Webkit RefPtr and PassRefPtr Basic的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!