【问题标题】:Smarty (and other tpl ngins): assign and assign_by_refSmarty(和其他 tpl ngins):assign 和 assign_by_ref
【发布时间】:2011-04-26 18:25:48
【问题描述】:

这不仅仅是关于 Smarty,而且我猜大多数模板引擎都分配了变量。这更像是一个理论问题,而不是一个实践问题。我没有用例。

当您将一个大数组$a 分配给另一个变量$b 时,PHP 会发生什么? PHP复制数组?也许,只是也许,它在内部创建了一个指针。那么当你稍微改变$a 时会发生什么? $b 不应更改,因为没有 & 用于创建 $b。 PHP 是否只是将内存使用量增加了一倍??

更具体地说:当您将一个大数组从控制器 ($a) 分配给您的模板引擎 ($tpl->vars['a']) 并在视图中使用 (extract$a) 时会发生什么? PHP 的内存是不是只有三倍??

现在如果我通过引用分配所有变量会发生什么?我很高兴我的观点能够将数组更改回控制器(无论如何我都不会回到那里)。如果变量在模板引擎 ($tpl->vars['a']) 中发生变化,也可以。

通过引用分配所有变量是否更利于内存?性能更好?如果是这样:是否有可能出现奇怪的、不需要的副作用?

因为人们喜欢代码而不是故事:

// copies
$a = array( ... );
$tpl->assign('a', $a); // creates a copy (?) in $tpl->vars['a']

// pointer / by ref
$a = array( ... );
$tpl->assign_by_ref('a', $a); // creates a pointer in $tpl->vars['a'] because:

function assign_by_ref( $name, &$var ) {
  $this->vars[$name] = $var; // voila pointer?
}

我很确定 PHP 不介意大数组、副本和克隆,但在性能和内存方面:哪个“更好”?

编辑
对于对象,所有这些都无关紧要。对象总是通过引用自动分配。而且由于物体很热,也许这是一个过时的问题,但我很好奇。

更新
所以 PHP 使用 copy on write... 喜欢它。对象总是指针。当你:

$a = new BigObject;
$b = $a; // pointer, right?
$b->updateSomethingInternally(); // $b is now changed > what about $a?

这是否触发了写时复制?还是 $a 和 $b 仍然相同(如===)?

编辑
我可以得出结论,仅仅为了节省内存,通过 ref 分配真的不值得吗? PHP 本身就足够聪明?

编辑
复制、克隆、按引用等有趣的可视化:http://www.phpinsider.com/download/PHP5RefsExplained.pdf

【问题讨论】:

  • 谢谢尼克。忘记那个代码块了。
  • 解决您的最后一次编辑:是的,这不值得。 PHP 在一般情况下足够聪明。此外:您根本不必担心这一点,内存通常不是网站的问题。

标签: php smarty pass-by-reference template-engine


【解决方案1】:

PHP 使用一个称为写时复制的概念。 IE。如果您只是执行$a = $b,PHP 不会将$b 的整个值复制到$a。它只会创建某种指针。 (更准确地说,$a$b 都指向同一个 zval,refcount 将增加。)

现在,如果 $a$b 被修改,则该值显然不能再共享,必须复制。

因此,除非您不修改模板代码中的数组,否则不会进行复制。

一些进一步的说明:

  • 当心尝试通过盲目插入引用来优化代码。通常,它们会产生与您预期相反的效果。举例说明原因:

    $a = SOMETHING_BIG; // $a points to a zval with refcount 1 and is_ref 0
    $b = $a;            // $a and $b both point to a zval with refcount 2 and is_ref 0
    $c =& $a;           // Now we have a problem: $c can't just point to the same zval
                        // anymore, because that zval has is_ref to 0, but we need one
                        // with is_ref 1. So The zval gets copied. You now have $b
                        // pointing to one zval with refcount 1 and is_ref 0 and $a and
                        // $c pointing to another one with refcount 2 and is_ref 1
    

    所以与你想要的相反实际上发生了。您实际上是在分配额外的内存,而不是节省内存。通常很难判断添加引用是否会使它变得更好或更糟,因为通常很难跟踪指向一个 zval 的所有不同变量(它通常不像看起来那么容易,只需看看 debug_zval_dump 的示例函数。因此,实际上,唯一安全的方法是了解引用是否对性能有好处,是实际分析两个变体。

  • 对象和其他所有东西一样,在 PHP 中是按值传递的。你仍然是正确的,它们的行为是引用-like,因为对于对象,传递的这个值只是指向其他数据结构的指针。在大多数情况下,通过引用传递和类似引用的行为之间的区别并不重要,但还是有区别的。

这只是对该主题的简短介绍。您可以在blog post by Sara Golemon with the porvoking title "You're being lied to" 中找到对该主题的更深入分析。

【讨论】:

  • 哇...这比我预期的要先进一点=)但我明白了这一点(以及示例中的问题)。我知道 PHP 是个聪明的妈妈!我正在用一个附加问题更新我原来的问题 =)
  • @Rudie:我更新了我的答案,加入了一段关于对象的文章。但在您的示例中,$a$b 都会发生变化。这是可以看到类似引用行为的情况之一。请参阅 webbiedave 答案以查看证明它不是真实参考的示例。
  • 有意思,不知道这个!
【解决方案2】:

正如其他答案中提到的,PHP 采用写时复制。不过,我确实希望回答您帖子的这一部分:

对象总是自动地 通过引用分配。

这并不完全正确。它们被分配了一个指向对象的标识符

$a = new stdClass();
$b = $a; // $a and $b now share same identifier
$b = 0; // $b no longer contains identifier
var_dump($a); // outputs object

将此与通过引用分配进行对比:

$a = new stdClass();
$b =& $a; // $a and $b now share same reference
$b = 0; //
var_dump($a); // outputs int(0)

更新

在你的编辑中你问:

$a = new BigObject;
$b = $a; // pointer, right?
$b->updateSomethingInternally(); // $b is now changed > what about $a?

这是否触发了写时复制?或者 $a 和 $b 仍然相同(如 ===)?

因为$b 现在包含一个标识符,即现在“指向”与$a 相同的对象,所以$a 也会受到影响。从来没有涉及任何对象的复制。

【讨论】:

  • 这是一个非常好的观点!这不是新闻,而是一个非常好的观点。我只是指包含对象的 vars 的内容。
【解决方案3】:

PHP 在传递数组时使用写时复制,因此在修改数组之前不会使用额外的内存。抱歉,没有支持此声明的链接。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-07-14
    • 1970-01-01
    • 2022-06-22
    • 2018-12-11
    • 2014-08-15
    相关资源
    最近更新 更多