web-dev-qa-db-ja.com

Swift構造体を参照で渡しますか?

同様の質問を検討しましたが、満足する回答がありません。

構造体を参照で渡すことは可能ですか、それともお勧めですか?もしそうなら?

例の参照としてのコードは次のとおりです。

struct MyData {
    var contentId: Int = 0
    var authorId: Int = 0
    var image: UIImage = UIImage(named: "myimage")
}

ご覧のとおり、これを行う主な理由は、私のイメージがいたるところに増殖していないためです。

18
Esqarrouth

構造体は、inoutキーワードと&演算子を使用して参照渡しすることができます。

struct Test {
    var val1:Int
    let val2:String

    init(v1: Int, v2: String) {
        val1 = v1
        val2 = v2
    }
}

var myTest = Test(v1: 42, v2: "fred")

func change(test: inout Test) {
    // you can mutate "var" members of the struct
    test.val1 = 24

    // or replace the struct entirely
    test = Test(v1: 10, v2: "joe")
}
change(test: &myTest)
myTest // shows val1=10, val2=joe in the playground

重要な状況で必要なパフォーマンスを得る唯一の方法であることが証明できない限り、この方法はお勧めできません。

これを行っても、UIImageをコピーする負担を軽減できないことに注意してください。参照型を構造体のメンバーとして配置しても、値で渡す場合にのみ参照をコピーします。画像の内容をコピーしていません。

構造体のパフォーマンスについて知っておくべきもう1つの重要なことは、copy-on-writeです。 Arrayのような組み込み型の多くは値型ですが、非常に高性能です。 Swiftで構造体を渡す場合、変異するまで、構造体をコピーする負担はかかりません。

詳細については、 値の型に関するWWDCビデオ を参照してください。

43
Rikki Gibson

まず、構造体を渡すと、通常は値で渡されます。構造体のすべてのプロパティはコピーされますが、UIImageのような参照型であるプロパティへのポインタのみが複製されます(8バイトのみ)。

var data1 = MyData()
var data2 = data1
// both data1 and data2 point to the same image

また、コンパイラーはコードを最適化するため、構造体は内部的に参照渡しされ、必要に応じてコピーされます。

トップレベルのコードで構造体を参照渡しする例として、inoutパラメータを使用できます。つまり、inoutパラメータを参照渡しできますが、inoutを実装するSwiftの一般的な方法つまり、パラメーターは値で渡され、関数が返された後、再割り当てされます。

コンパイラーは関数呼び出しを最適化するため、状況によっては真の参照を取得できます。この最適化は、計算されない変数、またはwillSetdidSetなどのプロパティオブザーバーを持つ変数にのみ影響します(おそらく他の場合も)。

したがって、関数のスコープ内にない現在のインスタンスのみに依存する必要があります。

struct Test {
    var value = 0
}

// case 1
var myTest = Test()

// case 2
var myTest = Test() {
didSet { print("I was set") }
}

// case 3
var myTest: Test {
get{ return Test() }
set{ print("I set myself") }
}

func mutateTest(inout t: Test) {
    // directly referencing to myTest

    myTest.value // value = 0 in all cases

    t.value = 42
    myTest.value // value = 42 in case 1 ; value = 0 in case 2 and 3

    t = Test()   // nothing gets printed
    myTest.value // value = 0 in all cases

    t = Test()   // nothing gets printed
    t.value = 3  // value = 3 in case 1 ; value = 0 in case 2 and 3
}

changeTest(&myTest)
//case 2: "I was set", case 3 "I set myself" get printed now

myTest.value // value = 3 in all cases

ご覧のように、myTesttは、ケース1(「true」参照セマンティクス)の場合にのみ同等です。したがって、関数自体でmyTestも参照する場合、これは大きな違いになります。しかし、これを行わない限り、行ってもかまいません。

5
Qbyte