可序列化单例模式的遗留问题答案
2011-03-18 00:00
1096 查看
在上一篇文章Serialize/Unserialize破坏单例的最后, 我留下了一个问题, 为了让大家能思考, 我就单独再写一篇给出答案.
上一篇中, 我们说到, 为了实现一个支持序列化的单例模式, 我们采用了如下的定义方式:
我之前的文章深入理解PHP原理之变量分离/引用(Variables Separation)中曾经介绍过, 在PHP中, 采用引用计数的方式来减少对内存的使用和提高效率.
回头来看这个问题, 根据运算符的结合律, 我们来单步分析这个过程:
在我们调用unserialize(serialize($a))的时候, 在serialize之前, PHP会首先尝试调用我们的类的实例$a的__sleep方法, 因为我们没有定义此方法, 所以跳过此步骤..
接下来, 在unserialize的时候, PHP在完成对象的创建以后, 会来调用新创建对象的__wakeup方法, 在这里面, 我们释放了原有的self::$instance的引用, 改变成了新的对象.
这个时候, 原来的$a, 并不会被释放, 因为此时符号名a还保留着对$a(单列类的一个实例)的引用, 但此时$a所指的对象的引用计数已经-1, 变成了1, (应该还要了解到, 此时, 还会对Object Store中的对象引用计数-1, 也变为了1)
最后, 我们把得到的新对象给$a赋值, OK, 关键的时候来了, 这个时候, 因为我们重新对$a赋值, 所以$a会释放之前所值向的zval的引用, 造成了此时这个zval的引用计数变为了零, 于是PHP就会释放这个zval, 也就会调用了Singleton的析构函数, 在这个析构函数中, 我们释放了静态实例$instance..
现在明白了么?
当然, 最后写成这样:
上一篇中, 我们说到, 为了实现一个支持序列化的单例模式, 我们采用了如下的定义方式:
class Singleton { private static $instance = NULL; /** 不容许直接调用构造函数 */ private function __construct() { } /** 不容许深度复制 */ private function __clone() { } public function __wakeup() { self::$instance = $this; } /** 需要在单利切换的时候做清理工作 */ public function __destruct() { //清理工作 .... self::$instance = NULL; } public static function getInstance() { if (NULL === self::$instance) { self::$instance = new self(); } return self::$instance; }}但这样的看似正确的代码, 确在某些时候达不到我们想要的结果:
$a = Singleton::getInstance();$a = unserialize(serialize($a));var_dump($a === Singleton::getInstance());//bool(false)那么为什么呢?
我之前的文章深入理解PHP原理之变量分离/引用(Variables Separation)中曾经介绍过, 在PHP中, 采用引用计数的方式来减少对内存的使用和提高效率.
回头来看这个问题, 根据运算符的结合律, 我们来单步分析这个过程:
在我们调用unserialize(serialize($a))的时候, 在serialize之前, PHP会首先尝试调用我们的类的实例$a的__sleep方法, 因为我们没有定义此方法, 所以跳过此步骤..
接下来, 在unserialize的时候, PHP在完成对象的创建以后, 会来调用新创建对象的__wakeup方法, 在这里面, 我们释放了原有的self::$instance的引用, 改变成了新的对象.
这个时候, 原来的$a, 并不会被释放, 因为此时符号名a还保留着对$a(单列类的一个实例)的引用, 但此时$a所指的对象的引用计数已经-1, 变成了1, (应该还要了解到, 此时, 还会对Object Store中的对象引用计数-1, 也变为了1)
最后, 我们把得到的新对象给$a赋值, OK, 关键的时候来了, 这个时候, 因为我们重新对$a赋值, 所以$a会释放之前所值向的zval的引用, 造成了此时这个zval的引用计数变为了零, 于是PHP就会释放这个zval, 也就会调用了Singleton的析构函数, 在这个析构函数中, 我们释放了静态实例$instance..
现在明白了么?
当然, 最后写成这样:
class Singleton { private static $instance = NULL; /** 不容许直接调用构造函数 */ private function __construct() { } /** 不容许深度复制 */ private function __clone() { } public function __wakeup() { self::$instance = $this; } /** 需要在单利切换的时候做清理工作 */ public function __destruct() { //只做清理工作. } public static function getInstance() { if (NULL === self::$instance) { self::$instance = new self(); } return self::$instance; }}
相关文章推荐
- 可序列化单例模式的遗留问题答案
- 关于:“无法序列化会话状态。在“StateServer”或“SQLServer”模式下,ASP.NET 将序列化会话状态对象,因此不允许使用无法序列化的对象或 MarshalByRef 对象。如果自定义会话状态存储在“Custom”模式下执行了类似的序列化,则适用同样的限制。”的问题
- 单例模式序列化后反序列化单例失效的问题
- XStream单例模式下反序列化Object-Mapping问题
- Pyke 逻辑编程入门(8):模式匹配之“病理学问题的答案”
- 单例模式再讨论(有关序列化的单例问题)
- 真正的创新必然是基于对市场的了解,对客户反馈的观察,开发出来的产品一定要适应市场,提出的模式一定要能解决现实的问题。而在这其中,技术只是一种实现手段。
- .net下灰度模式图像在创建Graphics时出现:无法从带有索引像素格式的图像创建graphics对象 问题的解决方案。
- Java对象序列化追加的问题,以及Java的读取多个对象的问题解决方法。
- c#的一些问题和参考答案
- Mongodb总结5-通过装饰模式,用Mongodb解决Hbase的不稳定问题
- jquery form表单.serialize()序列化后中文乱码问题原因及解决
- HNOI2006-公路修建问题(二分答案+并查集)
- Chrome浏览器部分手机浏览模式下元素间出现间距的问题
- 分层模式中的常见问题
- 关于NVIDIA 的 OpenGL回退到软件模式的问题。
- spring boot data RedisTemplate 序列化问题
- esp8266模式问题
- TreeSet自定义Comparator后序列化与反序列化问题
- HA模式下,扩展HDFS集群遇到的问题,namenode 启动不起来!emmmmmmmm~