2017-05-06 67 views
0
結構

比方說,我有一個模型,用String類型的屬性color稱爲Car商店參考斯威夫特

class Car { 
    var color: String? 
} 

現在,我有另一個類稱爲Interface,我想用它來修改數值的color財產Car。要做到這一點,我想以某種方式結合colorInterface的屬性,如key

class Interface { 

    var storedKey: String? 

    func bind(key: String?) { 
     self.storedKey = key 
    } 
} 

用法:

let corolla = Car() 
corolla.color = "green" 
let interface = Interface() 
interface.bind(key: &corolla.color) 
interface.storedKey = "blue" 
print(corolla.color) // still "green"! :(

因爲字符串值類型(結構)在這種方法失敗Swift,因此bind函數獲得corolla.color的副本,而不是參考。這意味着修改storedKeyinterfacecorolla.color的值沒有影響。

如果我們將inout添加到如下所示的綁定函數中,我們基本上獲得了傳入的密鑰的引用(it's actually copied in then copied out),但它只是作用於該函數的作用域。

func bind(key: inout String?) { 
    self.storedKey = key // assignment of value type is a copy! :/ 
} 

結果是一樣的,修改storedKey對它所綁定的屬性沒有影響。

這可以歸結爲什麼,我們可以在Swift中獲得對struct的引用嗎?


由於結構佔用的內存空間,我們也許能夠獲得一個指向傳遞的關鍵,這裏是工作(不正確)解決方案:

class Car { 
    var color: String? 
} 

let corolla = Car() 
corolla.color = "green" 

class Interface { 

    var storedKey: UnsafeMutablePointer<String?>? 

    func bind(_ key: inout String?) { 
     self.storedKey = withUnsafeMutablePointer(to: &key) { ptr in 
      return ptr 
     } 
    } 
} 

var interface = Interface() 
interface.bind(&corolla.color) 
interface.storedKey?.pointee = "blue" // :O it's... working? 
print(corolla.color) 

在我看來,這打破了至少兩個語義規則在斯威夫特:

  1. withUnsafeMutablePointer通過&傳遞一個實例的地址轉換爲該實例的類型爲「」,僅在使用時纔在之間提供關閉。按照the Swift docs

    body的指針參數只對封閉的生命週期有效。不要將它從閉合物中脫出以供以後使用。

  2. 第二個問題是,這利用了Swift編譯器優化,允許bind實際獲得對key的引用而不是副本。否則,指針不會獲得對原始鍵的引用,而是獲得對副本的引用。 inout參數通常使用稱爲拷入拷貝的行爲來實現,其中參數的拷貝被傳遞給函數,可能被修改,然後被分配回原始參數。 As per the docs,有一個優化,如果參數是存儲的屬性,則在函數體內部和外部使用相同的存儲位置。這使得我們的指針可以獲得對原始String結構的引用,而不是副本,從而可以工作。

    作爲優化,當參數是存儲在內存中物理地址的值時,函數體內部和外部都使用相同的內存位置。

有什麼辦法來存儲爲String這樣一個結構的參考?還是我在瘋狂的追逐?編輯1:另外,我不能控制Car的屬性。

+1

比較[斯威夫特:通過指針訪問計算屬性](http://stackoverflow.com/q/42798354/2976878) - 你可能只想要一個閉包。 – Hamish

+1

您可能正在尋找「鏡頭」:http://chris.eidhof.nl/post/lenses-in-swift/ –

回答

0

您不能像您已經研究的那樣獲得對您的結構的引用。

你可以做的就是使用Box類並傳遞它。

然後你可以在任何地方使用Box並在任何需要的地方訪問它的struct屬性。

喜歡的東西:

class Box<T> { 
    var value: T 
    init(value: T) { 
     self.value = value 
    } 
} 

,並用汽車使用

let boxedCar = Box(value: Car(...)) 

您可以共享引用此框,並隨時通過汽車通過價值性能

+0

我看過這種方法,但它不起作用,因爲寫入'value'屬性box類對綁定的'String'沒有影響,因爲賦值'self.value = value'是一個副本。 – paulvs

+0

你正在裝上已經是班級的'Car'。我曾想過裝箱汽車的屬性,但我無法控制'Car'屬性的類型(我將編輯問題以反映這一點)。 – paulvs

+0

如果你總是通過盒子訪問汽車,那麼修改結構(並因此只是設置一個新的屬性),每個人都可以通過盒子訪問它,不是嗎? –