Webkit RefPtr and PassRefPtr Basic

来源:互联网 发布:淘宝包邮换货运费谁出 编辑:程序博客网 时间:2024/06/02 04:20

RefPtr and PassRefPtr Basics

历史:

webkit当中很多对象是使用引用计数方式。这种方式的实现是通过每个类当中拥有refderef成员函数用来递增和减小引用计数。每一个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.当被赋值给对象时,引用计数递增为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可以trickRelease方法设置原始的RefPtr0, 并且构造一个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);

一般来说,RefPtrPassRefPtr,强制一个简单的规则,他们总是平衡在rel,deref调用,保证程序员不会错过deref调用。但是在这个case当中我们有一个指针类型,已经拥有引用计数,而且想交换拥有权,adoptRef函数可以使用。

// warning, requires a pointer that already has a ref

RefPtr<Node> node = adoptRef(rawNodePointer);

如果需要交换一个RefPtr到一个指针类型,而不需要更改引用计数,则可以通过调用PassRefPtrleadRef函数

// 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();

由于我们实现了adoptRefpassRef方法,这是一个有效的习惯。对象开始的引用计数为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 实现一个运行时的检查:如果我们创建一个对象并调用refderef方法之前,没有调用adoptRef将会收到assert failure.

使用指南(RefPtr,PassRefPtrwebcore中的使用)

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_firstChildm_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

原创粉丝点击