2017-04-12 8 views
2

我明白斯威夫特卡倫特,靜態瓦爾隱含懶:https://stackoverflow.com/a/34667272/1672161在斯威夫特,爲什麼賦值給一個靜態變量也援引其吸氣劑

但我不是,爲什麼出現這種情況清楚:

protocol HatType {} 

class Hat: HatType { 
    init() { print("real hat") } 
} 

class MockHat: HatType { 
    init() { print("mock hat") } 
} 

struct HatInjector { 
    static var hat: HatType = Hat() 
} 

HatInjector.hat = MockHat() 

// Output: 
// real hat 
// mock hat 

我所看到的是,靜態變量的賦值也在某種意義上調用了getter。這對我來說並不直觀。這裏發生了什麼?爲什麼這項任務只發生?

+0

這是一個班級項目嗎?其他人剛剛發佈http://stackoverflow.com/questions/43373932/setting-lazy-static-variable-first-initializes-then-幾分鐘前提出同樣的問題。 – rmaddy

+0

哈!一定是巧合! @rmaddy。結束,因爲那個人有更多的討論,所以更好地繼續那一個 – guptron

+0

我個人不認爲這是重複的 - 鏈接問答並沒有詢問行爲的原因(儘管我同意它不是很好解釋和解決方案跨越不同的問題)。但我不認爲傻瓜標記是最好的解決方案 - 我建議我們重新開放。 – Hamish

回答

2

這是因爲靜態和全局存儲變量是當前的快捷方式(這是所有隨時更改)只給一個訪問由編譯器 - unsafeMutableAddressor,它獲得一個指向變量存儲的指針(這可以看到by examining the SIL or IR emitted)。

這僅僅訪問:

  1. 獲取一個指針到一個編譯器生成的全局標誌確定所述靜態變量是否已經被初始化。

  2. 使用此指針調用swift_once以及初始化靜態變量(這是您給它的初始化表達式,即= Hat())的函數。在蘋果平臺上,swift_once只需forwards onto dispatch_once_f

  3. 返回一個指向靜態變量存儲的指針,然後調用者可以自由讀取和修改 - 因爲存儲具有靜態生命週期。

所以它(或多或少)Objective-C的線程安全懶惰初始化模式的等效:

+(Hat*) hat { 

    static Hat* sharedHat = nil; 
    static dispatch_once_t oncePredicate; 

    dispatch_once(&oncePredicate, ^{ 
     sharedHat = [[Hat alloc] init]; 
    }); 

    return sharedHat; 
} 

的主要區別在於夫特還給的指針的存儲sharedHat(指向參考的指針),而不是sharedHat本身(只是對實例的引用)。

因爲這是一個而且只有存取器用於靜態和全局存儲變量,爲了執行賦值,Swift需要調用它來獲得指向存儲器的指針。因此,如果它尚未初始化 - 訪問者需要首先將其初始化爲其默認值(因爲它不知道主叫方將如何處理它),然後在主叫方,然後將其設置爲另一個值之前。

這種行爲確實有點不直觀,並且一直是filed as a bug。正如喬丹羅斯在報告評論中所說:

這是目前的設計,但它可能是值得改變的設計。

所以這種行爲可能會在未來的語言版本中發生改變。

+1

這個答案比我本來希望的要多!感謝將這些放在一起的努力。 – guptron

+0

樂意幫忙@guptron :) – Hamish

+0

你能否確認我的理解是否正確?除了'private'外,全局存儲變量存在於靜態區域中,而不是堆區域,雖然我認爲實例屬性存儲在堆中。這樣對嗎? –

0

相同的溶液中Setting lazy static variable first initializes then assigns?

嘗試延遲加載:

struct HatInjector { 
    private static var _hat: HatType? 
    static var hat: HatType { 
     get { return _hat ?? Hat() } 
     set(value) { _hat = value } 
    } 
} 

或者:

struct HatInjector { 
    private static var _hat: HatType? 
    static var hat: HatType { 
     get { 
      if _hat == nil { 
       _hat = Hat() 
      } 
      return _hat! 
     } 
     set(value) { _hat = value } 
    } 
} 

原因: 在你的代碼的靜態無功是不可選的。因此,使用它時,swift必須確保它不是零(swift是保存!)。因此編譯器請求你設置了一個初始值。您無法定義:

static var prop1: MyProtocol 

這將導致編譯器錯誤。如果定義

static var prop1: MyProtocol? 

這將是有效的,因爲它是

static var prop1: MyProtocol? = nil 
+1

將來,不要從重複問題發佈重複答案,而應投票結束重複。 – rmaddy