Swift – как избежать циклических ссылок в структурах с родительскими и дочерними отношениями?

У меня есть отношения, которые можно описать таким образом;

У одного Parent много Child (детей)

struct Parent {
    var name: String
    var cost: Int
    var productionCost: Int
    var income: Int
    var children: [Child] = [Child]()

    init(name: String) {
        self.name = name
    }
}

struct Child {
    var name: String
    var parent: Parent?
    var owner: Player?
}

// not used but the intention is to so a player can own a child
class Player {
   var name:
   var cards: [Child] = [Child]()
}

Родитель должен знать, какие дочерние элементы у него есть, и точно так же дочерний элемент должен знать, какому родителю он принадлежит.

Но на игровых площадках Swift я заметил потенциальную циклическую ссылку.

If I do;

var parent = Parent.init(name: "Parent #1")
var child = Child.init(name: "Child #1", parent: parent)

parent.children.append(child)

print (parent)

Свифт сообщает игровая площадка;

Изображение

Отладка показывает родителей-детей, которые, в свою очередь, показывают родителя и т. д.

Я понимаю, что циклические ссылки не являются плохими по наследству, но я хотел бы попытаться избежать их в описанных отношениях.

Мне нужен только первый родитель.

По другим подобным вопросам в StackOverflow я прочитал следующее:

  • «ваши дочерние объекты должны быть тупыми объектами данных, они ничего не знают о своих родителях и являются автономными»

  • "родитель просто содержит дочерние объекты, он не внедряется в них"

Но я не уверен, как это поможет, поскольку мне нужно знать, какому родителю принадлежит ребенок.

Мой вопрос;

Как я могу избежать циклической ссылки в структурах, когда мне нужно присвоить отношения обеим сторонам?


person zardon    schedule 18.05.2017    source источник
comment
Типы struct являются типами значений. Таким образом, ваша попытка сделать так, чтобы дочерний элемент имел ссылку на родителя, не совсем имеет смысла, потому что вы действительно создаете копию родителя. Такая двусторонняя связь имеет смысл только со ссылочными типами.   -  person Rob    schedule 18.05.2017
comment
И почему вы пытаетесь это сделать? Вы знаете, что таким образом ваша структура делает копии родителя? То, что вы хотите сделать, имеет смысл с классами, а не со структурами.   -  person Nebojsa Nadj    schedule 18.05.2017
comment
О, ладно, я думаю, вместо этого использовать класс? Но для какой стороны, родителя, ребенка или обоих?   -  person zardon    schedule 18.05.2017
comment
Хорошо, у меня сложилось впечатление, что структура была бы лучше; но я понимаю смысл копий   -  person zardon    schedule 18.05.2017
comment
Проект, над которым я работаю, — это игра, в которой есть набор карт в колоде. Каждая карта (дочерняя) имеет ссылку на то, в какой колоде (родительской) она находится. Но в моем запросе я абстрагировался от всего не относящегося к делу материала о картах и ​​колоде для моего запроса.   -  person zardon    schedule 18.05.2017
comment
Если дочерним элементам нужны ссылки на родителя, то родитель должен быть class. И как только вы введете эталонную семантику в свою модель, я, вероятно, создам оба класса (хотя теоретически дочерним элементом может быть struct). Но я бы сделал шаг назад и спросил себя, действительно ли вам нужно, чтобы дочерний элемент сохранял ссылку на своего родителя. Иногда вам это не нужно, и вы можете не жертвовать преимуществами семантики значений, если вам не нужно. Например, вам действительно нужно знать, в какой колоде находится карта? Или, имея дело с данной картой, вы всегда уже знаете, какую колоду используете?   -  person Rob    schedule 18.05.2017
comment
Это интересная мысль. Я сделал свои структуры теперь классами, чтобы я мог сохранять ссылки. Ссылка необходима, потому что игрок владеет несколькими картами. Ему нужно знать, какая карта у него есть. Таким образом, это ссылка, а не копия. Но сама карта потенциально может быть структурой, потому что существует несколько копий карт; и все, что меняется, это ценности. Я рассматриваю только ссылки на родителей и владельцев (игроков). Я поэкспериментирую с обоими на игровых площадках, проведу несколько простых тестов и пойду со ссылками или структурами, основанными на этом.   -  person zardon    schedule 18.05.2017
comment
Ничто из того, что вы описали до сих пор, не говорит мне, что вам действительно нужны эти двунаправленные ссылки, и поэтому вам могут вообще не понадобиться ссылочные типы. Например. Вам когда-нибудь нужно знать, у кого Q♡, или вам действительно нужно знать только, какие карты есть у игрока X? Но, эй, используйте ссылочные типы, если хотите. Многие из нас — Крусти и пришли из мира ссылочных типов, поэтому иногда сложно программировать в значимых типах. См. раздел Создание лучших приложений с помощью типов значений в Swift.   -  person Rob    schedule 18.05.2017
comment
Изменен код родительской структуры; чтобы было понятно, что родительский и дочерний объекты разные. Спасибо за видео, буду пересматривать   -  person zardon    schedule 18.05.2017
comment
Лучший способ, которым я могу описать отношения, которые я моделирую, таков: Factory -< Engines Engine >-Player Игрок держит карту (двигатель), но эта карта нуждается в ссылке на фабрику (родителя), чтобы мы могли выполнить one to many through; поэтому кажется, что ссылки - это то, что мне нужно.   -  person zardon    schedule 18.05.2017


Ответы (1)


Используйте для этого классы. Ваша структура создает копию родителя, а не ссылку.

class Parent {
    var name: String
    var children: [Child] = [Child]()

    init(name: String) {
        self.name = name
    }
}

class Child {
    var name: String
    weak var parent: Parent?
    init(name:String,parent: Parent) {
        self.name = name
        self.parent = parent
    }
}
person Nebojsa Nadj    schedule 18.05.2017
comment
Хорошо, понял, теперь я приму ваш ответ. Я думаю, что пытался быть слишком умным в использовании структур. Ответ принят. - person zardon; 18.05.2017
comment
Если вы действительно хотите использовать этот родитель, имеет массив дочерних элементов, а дочерний элемент имеет ссылку на родительскую модель, я думаю, что опасно иметь init (или любой другой метод в этом отношении), который устанавливает одно отношение, а не другое. Это верный способ попасть в беду. Вы хотите убедиться, что любые методы, которые изменяют parent Child, также изменяют children затронутых родителей. Если вы предоставляете методы, которые влияют на одно отношение, а не на другое, вы только умоляете их рассинхронизироваться. - person Rob; 18.05.2017
comment
В этом примере я просто сделал его код 1:1 в структурах, но есть десятки способов сделать его лучше. Мы объяснили ему, что он не может использовать структуры. И эта ссылка была не ссылкой, а новой копией. В этом был весь смысл его вопроса. Он сказал, что хорошо знает, как эти вещи работают. Но он задал вопрос, на который нужно было ответить иначе, чем он изначально хотел. Итак, самый простой ответ с указанием этой справочной части ответа. Но я понимаю, что вы говорите. Не стесняйтесь редактировать мой ответ, если хотите. - person Nebojsa Nadj; 18.05.2017