Копия массива изменяет оригинал - PullRequest
0 голосов
/ 26 ноября 2018

У меня есть два контроллера.Первый контроллер загружает список с сервера и создает список пользовательских объектов, WordList.

class WordList {

    let name: String
    let releaseDate: Date
    var words: [String]
    let multiplier: Int

    ...
}

. На первом экране пользователю предоставляется возможность выбрать списки перед продолжением.На следующем контроллере случайное слово выбирается из случайного списка.После того, как слово представлено, оно удаляется, пока пользователь взаимодействует с ним.Как только пользователь перестает взаимодействовать, выбирается новое слово, пока его больше нетЕсли я вернусь к главному контроллеру и выберу тот же список, с которым я только что работал, список будет пустым.Вот как я отправляю выбранные элементы.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if let destination = segue.destination as? WordController {
        var wordLists = [WordList]()

        for index in tableView.indexPathsForSelectedRows! {
            wordLists.append(lists[index.row]) // lists is a class property that is a WordList array.
        }

        // This was my first attempt. Values were copied but removed from this controller.
        // for wordList in wordLists {
        //   destination.wordLists.append(wordList)
        // }

        // destination.wordLists = wordLists

        // This was my second attempt. Values were also copied but removed from this controller.
        destination.wordLists.append(contentsOf: wordLists)
    }
}

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

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

1 Ответ

0 голосов
/ 26 ноября 2018

Вы сказали:

Я понимаю, что должен передавать ссылку на список, а не копировать ее ...

Нет, вы передаетеновый массив.

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

К сожалению, вы не «копируете значения» из первого массива, а копируете ссылки WordList из первого массива во второй массив.Суть в том, что проблема не в Array, который является типом значения, а в WordList, который является ссылочным типом.

WordList является ссылочным типом, поскольку он являетсяclass.Таким образом, когда вы берете ссылку на WordList из одного массива и добавляете ее в другой массив, этот второй массив будет по-прежнему ссылаться на те же самые экземпляры WordList.

Если вы не хотите, чтобы ваши манипуляции сэкземпляры этого другого массива, влияющие на исходные экземпляры, вы можете:

  • изменить WordList с ссылочного типа (a class) на тип значения (a struct):

    struct WordList {
        let name: String
        let releaseDate: Date
        var words: [String]
        let multiplier: Int
    }
    
  • если вам действительно нужно использовать class, напишите свой собственный метод copy, который возвращает новый экземпляр.Например, вы можете соответствовать NSCopying и написать copy(with:):

    extension WordList: NSCopying {
        func copy(with zone: NSZone? = nil) -> Any {
            return WordList(name: name, releaseDate: releaseDate, words: words, multiplier: multiplier)
        }
    }
    

    , а затем при создании нового массива добавлять копии, а не ссылки на исходные экземпляры:

    for index in tableView.indexPathsForSelectedRows! {
        wordLists.append(lists[index.row].copy() as! WordList)
    }
    

Если вам не нравится тот факт, что NSCopying вводит этот неуклюжий Any тип возврата для copy, вы также можете просто определить свой метод copy или даженапишите свой собственный Copying протокол, например:

protocol Copying {
    associatedtype ObjectType = Self
    func copy() -> ObjectType
}

extension WordList: Copying {
    func copy() -> WordList {
        return WordList(name: name, releaseDate: releaseDate, words: words, multiplier: multiplier)
    }
}

, и тогда вы можете делать следующее, без необходимости приведения:

for index in tableView.indexPathsForSelectedRows! {
    wordLists.append(lists[index.row].copy())
}
...