Быстрая передача структуры по ссылке?

Я посмотрел на похожие вопросы, но я не видел ответа, который мне доволен.

Возможно или целесообразно передать структуры по ссылке? Если да, то как?

Вот пример для примера:

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

Как вы понимаете, моя основная причина заключается в том, что у меня нет изображения, умножающегося повсюду.

Ответ 1

Структуры могут передаваться по ссылке с использованием ключевого слова 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, выполнив это. Когда вы ставите ссылочный тип как член структуры, вы по-прежнему копируете ссылку только при ее передаче по значению. Вы не копируете содержимое изображения.

Еще одна важная вещь, которую нужно знать о производительности структуры, - copy-on-write. Многие встроенные типы, такие как Array, являются типами значений, и все же они очень эффективны. Когда вы передаете структуру в Swift, вы не подвергаетесь бремени копирования, пока не будете мутировать ее.

Просмотрите видео WWDC по типам значений, чтобы узнать больше.

Ответ 2

Прежде всего, если вы передаете структуру, она обычно передается по значению. Несмотря на то что все свойства структуры копируются только указатель на свойства, которые являются ссылочными типами, такими как UIImage, дублируется (всего 8 байтов):

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

Также компилятор оптимизирует код, поэтому структуры получают внутренне переданные по ссылке и копируются при необходимости.

В качестве примера передачи структуры по ссылке в коде верхнего уровня вы можете использовать параметры inout: Таким образом, параметр inout может передаваться по ссылке, но общий способ реализации inout для Swift состоит в том, что параметр получает значение pass и после возвращения функции возвращается.

Хотя компилятор оптимизирует вызов функции, чтобы вы могли получить истинную ссылку в некоторых ситуациях. Эта оптимизация влияет только на переменные, которые не вычисляются или имеют наблюдателя свойств, такие как willSet и didSet (и, возможно, другие случаи).

Таким образом, вы должны полагаться только на текущий экземпляр, который не входит в объем функции:

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

Как вы можете видеть, myTest и t эквивалентны только в случае 1 ( "истинная" эталонная семантика). Так что это имеет огромное значение, если вы также ссылаетесь на myTest в самой функции. Но пока вы этого не делаете, вам хорошо идти.