我們經常採用如下方式定義單例
class
singleton
/** 不容許深度複製 */
private
function
__clone
()
public
static
function
getinstance
()
return
self::$instance;
} }
很多人都會記得對深度copy的保護, 但, 其實我們卻疏忽了一點
$a = singleton::getinstance();
$b = unserialize(serialize($a));
var_dump($a === $b);
//bool(false)
呵呵, 可見還需要修補, 加上對序列化的保護
class
singleton
/** 不容許深度複製 */
private
function
__clone
() /** 不容許serialize */
private
function
__sleep
() /** 不容許unserialize */
private
function
__wakeup
() public
static
function
getinstance
() return
self::$instance;
}}
然而, 有的時候我們是希望我們的單例類是能序列化的, 這個時候可以考慮如下的方式
class
singleton
/** 不容許深度複製 */
private
function
__clone
() public
function
__wakeup
() /** 需要在單例切換的時候做清理工作 */
public
function
__destruct
() public
static
function
getinstance
() return
self::$instance;
}}
請注意上面, 我們在wakeup的時候, 切換了當前的單例例項, 來實現在序列化/反序列化的時刻保證單例。
另外, 對於一些包含全域性資源的單例類, 我們需要定義析構函式, 來在切換的過程中做資源**工作。
現在, 請大家仔細看看, 然後想想這段**有沒有什麼問題?
接著往下看, 這段**在有些條件下, 可能會達不到我們預期的目標, 比如
$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
function
__clone
() public
function
__wakeup
() /** 需要在單例切換的時候做清理工作 */
public
function
__destruct
() public
static
function
getinstance
() return
self::$instance;
}}
Serialize Unserialize破壞單例
作者 laruence 我們經常採用如下方式定義單列 1.class singleton 8.9.不容許深度複製 10.private function clone 12.13.public static function getinstance 17.18.return self instance...
單例模式,解決單例破壞。
破壞單例模式的三種方法 執行緒安全情況下 單例模式有 3 個特點 單例類只有乙個例項物件 該單例物件必須由單例類自行建立 單例類對外提供乙個訪問該單例的全域性訪問點。單例模式的優點和缺點 單例模式的優點 單例模式可以保證記憶體裡只有乙個例項,減少了記憶體的開銷。可以避免對資源的多重占用。單例模式設定...
C 反射破壞單例
單例大家都不陌生,程式從開啟到死亡過程中只能存在乙個例項,即存在不可建立,今天給大家介紹一種打破這種模式的方法,在程式執行中建立無數個單例例項物件。關於單例模式模糊或者不懂的可以參考c 單例模式 上文我們講到了反射的基本操作,例項化物件 屬性 方法 特性等操作,可以參考c 反射 咱們切入正題,單例有...