Слабая переменная выхода теряется (= ноль), когда упоминается в методе делегата - PullRequest
0 голосов
/ 02 мая 2019

В моем классе UICollectionView объявлен как @IBOutlet слабый var artworkCollectionView: UICollectionView!

Внутри этого класса есть один метод делегата, вызываемый двумя другими контроллерами представления, один из этих VC является всплывающим, а другой - обычным VC.

Метод делегата получает некоторые данные из базы данных, а затем обновляет представление коллекции, вызывая внутри замыкания: self.artworkCollectionView.reloadData ()

Когда метод делегата вызывается из всплывающего VC, все отлично работает. НО, когда метод делегата вызывается обычным VC, когда он попадает в self.artworkCollectionView.reloadData (), он получает печально известную фатальную ошибку: неожиданно найден ноль при неявном развертывании необязательного значения.

Я проверил все ссылки на ячейку reuseIdentifier, и все правильно. Я подозреваю, что, поскольку UICollectionView объявлен как слабый var, когда я перехожу из текущего класса во всплывающее окно, а затем всплывающее окно вызывает методы делегата, ссылка не теряется, но когда я перехожу из текущего класса в обычный VC, а затем обычный VC вызывает метод делегата, ссылка на мою слабую переменную теряется, и поэтому он «рассматривается» как ноль.

@IBOutlet weak var artworkCollectionView: UICollectionView!

override func viewDidLoad() {
    super.viewDidLoad()
    // Set up
    artworkCollectionView.dataSource = self
    artworkCollectionView.delegate = self
    artworkCollectionView.isUserInteractionEnabled = true
    artworkCollectionView.allowsSelection = true
    artworkCollectionView.register(UINib(nibName: 
    "MyCollectionViewCell", bundle: nil), 
    forCellWithReuseIdentifier: "cell")
}


// delegate method
func reloadCollections() {

    retrieveAlbumRatings { (isAlbum) in
        if isAlbum {

            self.retrieveAlbumData(completion: { (isFinished) in

                if isFinished {
                    // Reload collection views
                    self.artworkCollectionView.reloadData()

                }
            })
        }
    }
}

Если я прав, мой вопрос: как я могу дать слабую переменную artworkCollectionView: UICollectionView! СИЛЬНАЯ ссылка, чтобы она не потерялась в потоке от текущего класса к обычному VC и обратно?

РЕДАКТИРОВАТЬ: вот что я пытался до сих пор:

  1. Удалите «слабое» из объявления розетки, сделав его так: @IBOutlet var artworkCollectionView: UICollectionView! Но я получил ту же ошибку

  2. Я передал artworkCollectionView в обычный виртуальный канал через override executeSegue, а затем передал его обратно в качестве аргумента метода делегата. Это не дает мне фатальной ошибки, но также не перезагружает UICollectionView, потому что я думаю, что в любом случае слабая ссылка на выход UICollectionView потеряна.

Спасибо за вашу помощь (отказ от ответственности: я довольно новичок в Swift ..)

Ответы [ 2 ]

0 голосов
/ 03 мая 2019

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

Более длинный ответ заключается в том, что представление коллекции вашего контроллера представления должно сохраняться, пока контроллер представления находится на экране. Похоже, вы не очень хорошо понимаете жизненный цикл объекта и то, как работает ARC. Вы должны прочитать об этом и делать некоторые упражнения, пока не поймете это лучше.

Попробуйте что-то вроде этого:

//Make artworkCollectionView a normal weak var, not implicitly unwrapped. 
//You'll need to change your other code to unwrap it every time you use it.

@IBOutlet weak var artworkCollectionView: UICollectionView?

...

func reloadCollections() {

    retrieveAlbumRatings { (isAlbum) in
        if isAlbum {
            //The construct `[weak self]` below is called a capture list
            self.retrieveAlbumData(completion: { [weak self] (isFinished) in
                guard let weakSelf = self else {
                    print("self is nil");
                    return
                }
            }
            if isFinished {
                // Reload collection views
                guard let collectionView = weakSelf.artworkCollectionView else {
                    print("collectionView is nil!")
                    return
                }
                collectionView.reloadData()
            })
        }
    }
}
0 голосов
/ 03 мая 2019

Внутри этого класса есть один метод делегата, вызываемый двумя другими View Controllers, один из этих VC является всплывающим, другой - нормальный ВК.

Метод делегата получает некоторые данные из базы данных, а затем обновляет представление коллекции, вызывающее внутри замыкания: self.artworkCollectionView.reloadData ()

  1. Похоже, что у вас есть VC, содержащий приведенный выше код, VC может либо открыть всплывающее окно, либо просто выполнить стандартный push-переход к «обычному VC».
  2. Вы хотите, чтобы во всплывающем VC или обычном VC выполнялась какая-то операция, загружались некоторые данные, а затем, когда пользователь перенаправляется обратно в исходный VC, UICollectionView обновляется этими данными.

Ваши проблемы следующие:

Я передал artworkCollectionView в обычный VC через переопределение executeSegue, а затем передал его обратно в качестве аргумента делегата метод. Это не дает мне фатальную ошибку, но и не дает перезагрузить UICollectionView, потому что я думаю, что в любом случае слабый ссылка на выход UICollectionView потеряна. В большинстве случаев вы не должны распространять что-либо подобное, если только у вас нет для этого веской причины (я не вижу такой).

Вы хотите разделить проблемы здесь. Вы должны тщательно продумать, что вы хотите передать между венчурными капиталистами, чтобы избежать странных зависимостей между ними. Я не стал бы пропускать розетки по нескольким причинам, во-первых, теперь вам нужно отслеживать розетку в нескольких венчурных конторах, если вы когда-нибудь решите изменить ее. Во-вторых, для того, чтобы отслеживать состояние розетки, требуется слишком много умственной гимнастики, поскольку она раздается повсюду. Розетки также гарантированно устанавливаются только на определенных этапах жизненного цикла. Например, если вы извлекаете целевой VC из последовательности в prepareForSegue:sender: и пытаетесь сослаться на выходы в то время, все они будут равны нулю, поскольку они еще не были установлены.

Это все веские причины, по которым VC, содержащий приведенный выше код, должен быть единственным (и единственным), контролирующим то, что отображается в artworkCollectionView и когда. Проблема здесь в том, как вы подходите к этому, вместо того, чтобы всплывающий или обычный VC вызывал метод делегата или делал странные вещи, такие как передача розеток из одного VC в другой, просто вместо этого передавал данные.

Простейший пример:

  1. Всплывающий VC и обычный VC вызывают некоторый код, чтобы фактически получить данные.
  2. Затем в зависимости от того, как вы на самом деле переходите к всплывающему ВК или обычный VC из исходного VC, используйте либо parentViewController, либо presentingViewController чтобы получить ссылку на оригинальный VC.
  3. Установите данные в исходный VC через эту ссылку.
  4. При необходимости отклоните всплывающий или обычный виртуальный канал (зависит от вашего конкретного приложения, может быть, вы хотите, чтобы пользователь нажал UIButton, чтобы отклонить его вместо того, чтобы делать это для них).
  5. Когда оригинальный VC вернется в поле зрения, добавьте некоторый код в метод жизненного цикла, например viewWillAppear чтобы загрузить содержимое данных в UICollectionView в то время.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...