成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

【In PHP】析構(gòu)、作用域與引用

fai1017 / 1385人閱讀

摘要:在編寫(xiě)一段析構(gòu)方法的研究代碼中,我遇到了交叉知識(shí)點(diǎn)導(dǎo)致的錯(cuò)誤在不同作用域,析構(gòu)方法與引用次數(shù)導(dǎo)致了不一樣的結(jié)果。前提本文假裝你已經(jīng)明白什么是析構(gòu)方法作用域及引用次數(shù)。當(dāng)析構(gòu)函數(shù)的調(diào)用鉤子去檢測(cè)引用數(shù)時(shí),全局的實(shí)例自然無(wú)法觸發(fā)這個(gè)事件。

在編寫(xiě)一段析構(gòu)方法的研究代碼中,我遇到了交叉知識(shí)點(diǎn)導(dǎo)致的錯(cuò)誤——在不同作用域,析構(gòu)方法與引用次數(shù)導(dǎo)致了不一樣的結(jié)果。
前提

本文假裝你已經(jīng)明白什么是析構(gòu)方法、作用域及引用次數(shù)。關(guān)于后者,引用次數(shù)是 PHP 垃圾收集中的重要機(jī)制,它很大程度上,幫助 PHP 在程序運(yùn)行時(shí)清理內(nèi)存垃圾(參考:引用計(jì)數(shù)基礎(chǔ) - PHPDoc)。

正文 有誤的測(cè)試

來(lái)看這段代碼:

class A {
    public $var = [];

    public function __construct()
    {
        echo "__construct: " . spl_object_hash($this) . "
";
    }

    public function __destruct()
    {
        echo "__destruct: " . spl_object_hash($this);
    }

    public function test()
    {
        throw new Exception("Hello");
    }
}

$test = new A();
$test->test();

我的本意是“在拋出不捕獲的異常時(shí),析構(gòu)方法是否正常執(zhí)行”。結(jié)果是沒(méi)有執(zhí)行,OK,很穩(wěn):

__construct: 0000000045f0af9e00000000494744b0
Fatal error: Uncaught Exception: Hello in...

當(dāng)我們以為事情就此結(jié)束,后續(xù)往往會(huì)接踵而來(lái)。

翻車(chē)的代碼

在公司前輩指出“你這段代碼有問(wèn)題,犯了作用域的錯(cuò)誤”之后,我是當(dāng)場(chǎng)宕機(jī)的。


啥,作用域?析構(gòu)方法?我是不是聽(tīng)錯(cuò)了,那玩意不是變量的概念么。

經(jīng)過(guò)我的追問(wèn),前輩告訴我:你把執(zhí)行代碼放到函數(shù)里試試。

避免文章過(guò)長(zhǎng),直接上差異部分的代碼,如下:

class A {
    // 與之前一致
}

function test()
{
    $test = new A();
    $test->test();
}

test();

結(jié)果如下:

__construct: 000000004b11d811000000006f9a75c7
__destruct: 000000004b11d811000000006f9a75c7
Fatal error: Uncaught Exception: Hello in...

心態(tài)如下:


說(shuō)好的不執(zhí)行呢?真是令人絕望。

當(dāng)場(chǎng)打臉,只好去琢磨“析構(gòu)方法的作用域”是個(gè)啥。搜索結(jié)果里看到了這樣的話(huà):

析構(gòu)函數(shù)會(huì)在到某個(gè)對(duì)象的所有引用都被刪除或者當(dāng)對(duì)象被顯式銷(xiāo)毀時(shí)執(zhí)行。來(lái)源:構(gòu)造與析構(gòu)函數(shù) - PHPDoc

讓我們推理一下:

函數(shù)結(jié)束后,該函數(shù)級(jí)別的作用域就結(jié)束了,而此時(shí)腳本還未結(jié)束。沒(méi)有任何引用的對(duì)象實(shí)例,自然可以執(zhí)行析構(gòu)方法;

全局作用域則不一樣,所以導(dǎo)致了對(duì)象在全局作用域結(jié)束后,沒(méi)機(jī)會(huì)調(diào)用析構(gòu)方法。

結(jié)果似乎明朗了。

深入

當(dāng)然,淺嘗輒止可對(duì)不起我的好奇心。既然要搞明白這個(gè)問(wèn)題,那就問(wèn)一問(wèn)核心問(wèn)題:

證實(shí):函數(shù)級(jí)別的作用域結(jié)束與對(duì)象執(zhí)行析構(gòu)方法,是否有必然聯(lián)系?

新問(wèn)題:調(diào)用析構(gòu)方法與結(jié)束變量,誰(shuí)先誰(shuí)后?


相信在理清上述兩個(gè)問(wèn)題的答案后,這個(gè)文章也就沒(méi)有存在的意義了,笑~

問(wèn)題一
函數(shù)級(jí)別的作用域結(jié)束與對(duì)象執(zhí)行析構(gòu)方法,是否有必然聯(lián)系?

很簡(jiǎn)單,咱們讓對(duì)象與函數(shù)的作用域脫鉤,就可以逆向地證實(shí)這一點(diǎn):

class A {
    // 與之前一致
}
$i = 123;

function test(&$i) // 通過(guò)引用機(jī)制,給函數(shù)的作用域增加污染變量
{
    $test = new A();
    $i = $test; // 將對(duì)象實(shí)例的引用擴(kuò)展到全局作用域
    $test->test();
}

test($i);

結(jié)果如下:

__construct: 0000000042a054c3000000001f53236f
Fatal error: Uncaught Exception: Hello in...

果然,當(dāng)引用計(jì)數(shù)不為 0 時(shí),析構(gòu)函數(shù)就不會(huì)被調(diào)用,賊穩(wěn)~

問(wèn)題二
新問(wèn)題:調(diào)用析構(gòu)方法與結(jié)束變量,誰(shuí)先誰(shuí)后?

這個(gè)問(wèn)題就有點(diǎn)意思了,熟悉程序的朋友又應(yīng)該明白,遇到這種“X的某個(gè)機(jī)制是什么時(shí)候觸發(fā)的”,就應(yīng)該去看X的生命周期,X 泛指一切。

在經(jīng)過(guò)一番查找,我從《PHP7內(nèi)核剖析》中找到了 PHP 的生命周期,注意我標(biāo)紅圈的兩個(gè)地方:

清理全局變量與析構(gòu)方法的調(diào)用,我們就找到了。

但此時(shí)困惑了我的問(wèn)題就變成了:普通變量到底什么時(shí)候銷(xiāo)毀?

我翻遍了 PHP 的生命周期、網(wǎng)絡(luò)上的文章,也沒(méi)找到想要的答案。大家都在聊全局變量的銷(xiāo)毀事件,難道全局的普通變量是弱勢(shì)群體嗎?

直到我看到 PHP 手冊(cè)上的范例:

// 使用 global

$a = 1;
$b = 2;

function Sum()
{
    global $a, $b;

    $b = $a + $b;
}

Sum();
echo $b;

原來(lái) 全局范圍的普通變量 = 全局變量,這結(jié)論真是令我頭禿。

最終總結(jié)一下:

當(dāng)實(shí)例的引用為 0 時(shí),會(huì)步入銷(xiāo)毀階段,此時(shí),析構(gòu)函數(shù)才會(huì)啟用;

當(dāng)對(duì)象的實(shí)例位于全局作用域,該變量會(huì)在 全局變量銷(xiāo)毀 事件中銷(xiāo)毀,在此之前,全局變量的引用數(shù)至少為 1;

析構(gòu)函數(shù)的調(diào)用 發(fā)生在 全局變量銷(xiāo)毀 之前。

當(dāng)析構(gòu)函數(shù)的調(diào)用鉤子去檢測(cè)“引用數(shù)”時(shí),全局的實(shí)例自然無(wú)法觸發(fā)這個(gè)事件。

至于為什么會(huì)犯這樣的錯(cuò)誤,原因也有兩個(gè):

對(duì) PHP 的生命周期認(rèn)知模糊不清;

不清楚 PHP 的全局變量如何定義。

為什么會(huì)犯這兩個(gè)錯(cuò)誤,自然也有理由,但無(wú)論什么理由,都解決不了在面對(duì)知識(shí)點(diǎn)交叉時(shí),因?yàn)橹R(shí)盲點(diǎn)所犯下的錯(cuò)。下次學(xué)東西,還是跟著官方文檔學(xué)習(xí)吧。


圖片出處源自網(wǎng)絡(luò)或水印,侵刪。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/29766.html

相關(guān)文章

  • PHP面試之面向?qū)ο螅?)

    摘要:二面向?qū)ο笥惺裁刺卣髅嫦驅(qū)ο蟮闹饕卣饔谐橄罄^承封裝和多態(tài)。析構(gòu)函數(shù)析構(gòu)函數(shù)是在引入的,它的作用與調(diào)用時(shí)機(jī)和構(gòu)造函數(shù)剛好相反,它在對(duì)象被銷(xiāo)毀時(shí)自動(dòng)執(zhí)行。 PHP面試專(zhuān)欄正式起更,每周一、三、五更新,提供最好最優(yōu)質(zhì)的PHP面試內(nèi)容。PHP中面向?qū)ο蟪?嫉闹R(shí)點(diǎn)有以下7點(diǎn),我將會(huì)從以下幾點(diǎn)進(jìn)行詳細(xì)介紹說(shuō)明,幫助你更好的應(yīng)對(duì)PHP面試常考的面向?qū)ο笙嚓P(guān)的知識(shí)點(diǎn)和考題。整個(gè)面向?qū)ο笪恼碌慕Y(jié)構(gòu)涉...

    phodal 評(píng)論0 收藏0
  • 重讀PHP手冊(cè)筆記系列(二)

    摘要:通過(guò)注冊(cè)自動(dòng)加載器,腳本引擎在出錯(cuò)失敗前有了最后一個(gè)機(jī)會(huì)加載所需的類(lèi)構(gòu)造函數(shù)和析構(gòu)函數(shù)構(gòu)造函數(shù)允行開(kāi)發(fā)者在一個(gè)類(lèi)中定義一個(gè)方法作為構(gòu)造函數(shù)。析構(gòu)函數(shù)會(huì)在到某個(gè)對(duì)象的所有引用都被刪除或者當(dāng)對(duì)象被顯式銷(xiāo)毀時(shí)執(zhí)行抽象類(lèi)支持抽象類(lèi)和抽象方法。 語(yǔ)言參考 1. 類(lèi)型 1.1 簡(jiǎn)介 showImg(https://segmentfault.com/img/bVbqlNJ?w=531&h=379);...

    馬龍駒 評(píng)論0 收藏0
  • php易錯(cuò)筆記-類(lèi)與對(duì)象,命名空間

    摘要:類(lèi)與對(duì)象基本概念如果在之后跟著的是一個(gè)包含有類(lèi)名的字符串,則該類(lèi)的一個(gè)實(shí)例被創(chuàng)建。如果該類(lèi)屬于一個(gè)名字空間,則必須使用其完整名稱(chēng)。如果一個(gè)類(lèi)被聲明為,則不能被繼承。命名空間通過(guò)關(guān)鍵字來(lái)聲明。 類(lèi)與對(duì)象 基本概念 new:如果在 new 之后跟著的是一個(gè)包含有類(lèi)名的字符串,則該類(lèi)的一個(gè)實(shí)例被創(chuàng)建。如果該類(lèi)屬于一個(gè)名字空間,則必須使用其完整名稱(chēng)。 Example #3 創(chuàng)建一個(gè)實(shí)例 ...

    MartinHan 評(píng)論0 收藏0
  • PHP面試??純?nèi)容之面向?qū)ο螅?)

    摘要:繼上一篇面試??純?nèi)容之面向?qū)ο蟀l(fā)表后,今天更新,需要的可以直接點(diǎn)擊文字進(jìn)行跳轉(zhuǎn)獲取。析構(gòu)函數(shù),當(dāng)對(duì)象被銷(xiāo)毀時(shí)調(diào)用。 PHP面試專(zhuān)欄正式起更,每周一、三、五更新,提供最好最優(yōu)質(zhì)的PHP面試內(nèi)容。繼上一篇PHP面試??純?nèi)容之面向?qū)ο螅?)發(fā)表后,今天更新(2),需要(1)的可以直接點(diǎn)擊文字進(jìn)行跳轉(zhuǎn)獲取。整個(gè)面向?qū)ο笪恼碌慕Y(jié)構(gòu)涉及的內(nèi)容模塊有: 一、面向?qū)ο笈c面向過(guò)程有什么區(qū)別?二、面向?qū)?..

    Barry_Ng 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<