前提须知:
- COW机制:Copy-on-Write,写入时才真正复制一份内存进行修改
- 引用:用不同的名字访问同一个变量内容(符号:&)
- zval的容器:所有的php变量都放在了zval的容器中,一个zval变量容器包含:类型、值、is_ref(代表这个"容器"是否被引用,1 引用 0 默认)、refcount(代表有多少变量指向这个"容器")
- xdebug_debug_zval():通过调用函数显示"refcount"和"is_ref"的值
- memory_get_usage():返回分配给 PHP 的内存量说明
COW机制--对应例子1:
<?php
$a = range(0,9999); //赋值变量$a
var_dump(memory_get_usage()); //打印已使用的内存
$b = $a; //赋值变量$b=$a,不会分配内存,因为没有对内容进行写入操作
var_dump(memory_get_usage()); //打印的内存不会明显发生改变
$a = range(0,9999); //对变量$a重新赋值
var_dump(memory_get_usage()); //内存发生明显变化
//输出结果:
int 5391568
int 5391568
int 6257216
COW机制--对应例子2:
<?php
// zval变量容器
$a = 1;
xdebug_debug_zval('a');
// 定义变量b,把a的值赋值给b
$b = $a;
xdebug_debug_zval('a');
xdebug_debug_zval('b');
// 修改a
$a = 2;
xdebug_debug_zval('a');
xdebug_debug_zval('b');
//输出结果:
a:
(refcount=1, is_ref=0),int 1
a:
(refcount=2, is_ref=0),int 1
b:
(refcount=2, is_ref=0),int 1
a:
(refcount=1, is_ref=0),int 2
b:
(refcount=1, is_ref=0),int 1
引用--对应例子1
<?php
$a = range(0,9999); //赋值变量$a
var_dump(memory_get_usage()); //打印已使用的内存
$b = &$a; //把$a的地址给$b,$a和$b指向同一个内存空间
var_dump(memory_get_usage()); //打印的内存不会明显发生改变
$a = range(0,9999); //只是改调了空间值,$a和$b指向的都是修改后的值
var_dump(memory_get_usage()); //打印的内存还是不会明显发生改变
//输出结果:
int 5391568
int 5391568
int 5391544
引用--对应例子2
<?php
// zval变量容器
$a = 1;
xdebug_debug_zval('a');
// 定义变量b,把a的值赋值给b
$b = &$a;
xdebug_debug_zval('a');
xdebug_debug_zval('b');
// 修改a
$a = 2;
xdebug_debug_zval('a');
xdebug_debug_zval('b');
//输出结果:
a:
(refcount=1, is_ref=0),int 1
a:
(refcount=2, is_ref=1),int 1
b:
(refcount=2, is_ref=1),int 1
a:
(refcount=2, is_ref=1),int 2
b:
(refcount=2, is_ref=1),int 2
引用--对应例子3
<?php
// unset 只会取消引用,不会销毁空间
$a = 1;
$b = &$a;
unset($b);
echo $a. "\n"; //还是会输出1
//输出内容:
1
引用--对应例子4
<?php
// 对象本身就是引用传递
class Demo
{
public $name = "demo1";
}
$d1 = new Demo;
xdebug_debug_zval('d1');
$d2 = $d1;
xdebug_debug_zval('d1');
xdebug_debug_zval('d2');
$d2->name = "demo2";
xdebug_debug_zval('d1');
xdebug_debug_zval('d2');
//输出结果:
d1:
(refcount=1, is_ref=0),
object(app\index\controller\Demo)[183]
public 'name' => (refcount=1, is_ref=0),string 'demo1' (length=5)
d1:
(refcount=2, is_ref=0),
object(app\index\controller\Demo)[183]
public 'name' => (refcount=1, is_ref=0),string 'demo1' (length=5)
d2:
(refcount=2, is_ref=0),
object(app\index\controller\Demo)[183]
public 'name' => (refcount=1, is_ref=0),string 'demo1' (length=5)
d1:
(refcount=2, is_ref=0),
object(app\index\controller\Demo)[183]
public 'name' => (refcount=1, is_ref=0),string 'demo2' (length=5)
d2:
(refcount=2, is_ref=0),
object(app\index\controller\Demo)[183]
public 'name' => (refcount=1, is_ref=0),string 'demo2' (length=5)
总结例子
<?php
/**
* 写出如下程序的输出结果
* <?php
*
* $data = ['a', 'b', 'c'];
*
* foreach($data as $key => $val)
* {
* $val = &$data[$key];
* }
* 程序运行时,每一次循环结束后变量$data的值是什么?请解释
* 程序执行完成后,变量$data的值是什么?请解释
*/
$data = ['a', 'b', 'c'];
foreach ($data as $key=>$val)
{
$val = &$data[$key];
var_dump($data);
}
var_dump($data);
//输出结果:
array (size=3)
0 => &string 'a' (length=1)
1 => string 'b' (length=1)
2 => string 'c' (length=1)
array (size=3)
0 => string 'b' (length=1)
1 => &string 'b' (length=1)
2 => string 'c' (length=1)
array (size=3)
0 => string 'b' (length=1)
1 => string 'c' (length=1)
2 => &string 'c' (length=1)
array (size=3)
0 => string 'b' (length=1)
1 => string 'c' (length=1)
2 => &string 'c' (length=1)
附加clone的用法
由于对象本身就是引用传递,
假设$obj1和$obj2是两个对象,使用$obj2=$obj1这样的方法复制出来的对象是相关联的
如果想让复制的对象$obj2修改属性时不会改变$obj1的属性,可以用$obj2=clone $obj1;
如果还希望在复制的同时,目标对象的某些属性与源对象的不同,可以在类里面定义一个__clone()方法,在这个方法中完成为目标对象的属性赋新值。
<?php
class demoClone{
private $id,$name;
public function __construct($id=0,$name=''){
$this->name=$name;
$this->id=$id;
}
public function get_id(){
return $this->id;
}
public function get_name(){
return $this->name;
}
/*当没有__clone和有__clone是有区别的*/
public function __clone(){
$this->id=$this->id+1;
$this->name='FJ';
}
}
$A = new demoClone(10,'A','UK');
echo '克隆之前的对象:';
echo 'id='.$A->get_id();
echo 'name='.$A->get_name();
echo "\n";
$B = clone $A;
echo '克隆过后的对象:';
echo 'id='.$A->get_id();
echo 'name='.$A->get_name();
echo "\n";
echo '克隆过后的对象属性:';
echo 'id='.$B->get_id();
echo 'name='.$B->get_name();
//输出结果:
//当没有__clone
克隆之前的对象:id=10name=HEHE
克隆过后的对象:id=10name=HEHE
克隆过后的对象属性:id=10name=HEHE
//当有__clone
克隆之前的对象:id=10name=HEHE
克隆过后的对象:id=10name=HEHE
克隆过后的对象属性:id=11name=FJ
内容是对慕课网视频教程《360大牛带你横扫PHP职场 全面解读PHP面试》的看后总结记录