2017-04-13 63 views
1

我在SWIFT 3.0中將我的模型實現爲structs。其中幾個structs的代表應該能夠根據用戶的操作修改模型。在Swift中傳遞值類型作爲參考

但是,當我將struct傳遞給delegate方法時,它會被複制。

你如何解決這個問題?你能強制編譯器通過這個struct作爲參考,還是唯一的選擇是使用class

+1

首先使用'struct'的重點在於,這是理想的行爲。它保留了數據的不變性。 'inout'可以做到這一點,但在一般情況下不推薦。爲什麼要複製一個問題? – Alexander

+0

那麼如何從控制器修改模型?如果你的委託方法像Cocoa一樣通過模型傳遞模型,那麼委託人(通常是一個控制器)如何修改_real_模型,而不是副本? – cfischer

+0

有點相關:[如何使用值類型對象作爲引用類型?](http://stackoverflow.com/q/41833469/2976878) – Hamish

回答

1

struct s總是按值傳遞。使用struct的重點在於使其表現爲一種值類型。如果你需要授權(通常意味着可變狀態),你應該使用一個類。

如果您確實需要,可以使用inout參數強制通過引用,但通常不建議這樣做。您也可以使用box type來模擬按引用傳遞。但是,一般來說,如果您需要引用行爲,則應該只使用一個類。

+0

*不使用'inout'時,無論類型如何,Everything *總是按值傳遞。 – newacct

+0

嗯,從技術上說是的,但是因爲當你傳遞一個類引用時,你實際上是通過值傳遞一個引用,它幾乎和傳遞引用一樣有效。 –

0

如果你想通過參考,你通常應該使用class而不是struct

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/ClassesAndStructures.html狀態:

您可以同時使用類和結構定義自定義數據類型 用作你的程序代碼的基石。

但是,結構實例總是按值傳遞,而 實例總是按引用傳遞。這意味着他們是 適合不同類型的任務。當您考慮項目所需的數據結構和功能時,請決定 每個數據結構是定義爲類還是定義爲 結構。

2

首先使用struct的重點在於,這是可取的行爲。它保留了數據的不變性。 inout可以實現這一點,但在一般情況下不推薦使用。

protocol Delegate { 
    func callback(_ oldValue: Int) -> Int 
} 

struct IncrementerDelegate: Delegate { 
    let step: Int 

    func callback(_ oldValue: Int) -> Int { 
     return oldValue + step 
    } 
} 

struct Model { 
    var i = 0 
} 

class Controller { 
    var model = Model() 
    var delegate: Delegate 

    init(delegate: Delegate) { 
     self.delegate = delegate 
    } 

    // To be called externally, such as by a button 
    func doSomething() { 
     // Delegate determains new value, but it's still the 
     // controller's job to perform the mutation. 
     model.i = delegate.callback(model.i) 
    } 
} 

let delegate = IncrementerDelegate(step: 5) 
let controller = Controller(delegate: delegate) 
print(controller.model.i) 
controller.doSomething() // simulate button press 
print(controller.model.i) 








protocol CrappyDelegate { 
    func callback(_ model: inout Model) 
} 

struct CrappyIncrementerDelegate: CrappyDelegate { 
    let step: Int 

    func callback(_ model: inout Model) { 
     model.i = 9999999 
     // Just hijacked the models value, 
     // and the controller can't do anything about it. 
    } 
} 

class VulnerableController { 
    var model = Model() 
    var delegate: CrappyDelegate 

    init(delegate: CrappyDelegate) { 
     self.delegate = delegate 
    } 

    // To be called externally, such as by a button 
    func doSomething() { 
     // Controller leaks entire model, and has no control over what happens to it 
     delegate.callback(&model) 
    } 
} 

let crappyDelegate = CrappyIncrementerDelegate(step: 5) 
let vulnerableController = VulnerableController(delegate: crappyDelegate) 
print(controller.model.i) 
controller.doSomething() // simulate button press 
print(controller.model.i) // model hijacked