Статьи

Swift: когда removeFromSuperview () недостаточно

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

На первый взгляд, я подумал, что это просто: все, что мне нужно сделать, это отфильтровать удаленный узел из массива объектов значений узла модели представления и вызвать r emoveFromSuperview ()  для самого виджета. 

Ааа, если бы это было, если бы это было .

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

            nodeWidget.removeFromSuperview()
            nodeWidget = nil



первый шаг в
том , чтобы сделать мой 
NodeWidget  класс реализации 
NilLiteralConvertible . Реализация этого протокола означает, что мой класс должен иметь 
метод
convertFromNilLiteral (),  который возвращает его собственную версию. Моя версия выглядит следующим образом
[см. Приложение ниже]

class func convertFromNilLiteral() -> Self
    {
        return self(frame: CGRectZero, node: NodeVO(name: "NULL_NODE", position: CGPointZero))
    }

Теперь, когда я установил экземпляр  NodeWidget  на  ноль , он правильно деинициализирован. В рамках этого процесса вызывается его  deinit,  и я также могу удалить любых наблюдателей:

    deinit
    {
        NodesPM.removeObserver(self) // invokes notificationCentre.removeObserver(observer)

    }

Я также использовал  UIView.animateWithDuration,  чтобы удаленный узел исчезал , а не внезапно исчезал. Для этого требуется временная переменная,  nodeWidgetPendingDelete , которая содержит ссылку на удаленный виджет, который удаляется из его суперпредставления и обнуляется после завершения замирания:

UIView.animateWithDuration(NodeConstants.animationDuration, animations: {self.nodeWidgetPendingDelete!.alpha = 0}, completion: deleteAnimationComplete)

    [...]


    func deleteAnimationComplete(value: Bool)
    {
        if (value && nodeWidgetPendingDelete != nil)
        {
            nodeWidgetPendingDelete?.removeFromSuperview()
            nodeWidgetPendingDelete = nil
        }

    }

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

static func deleteSelectedNode()
    {
        for node in nodes
        {
            node.inputNodes = node.inputNodes.filter({!($0 == NodesPM.selectedNode!)})
        }

        nodes = nodes.filter({!($0 == NodesPM.selectedNode!)})

        postNotification(.NodeDeleted, payload: selectedNode)
        postNotification(.RelationshipsChanged, payload: nil)

        selectedNode = nil

    }

Мой исходный код теперь обновлен и доступен в  моем репозитории GitHub здесь .
Добавление  Спасибо  @ChromophoreApp  за указание на то, что   реализация NilLiteralConvertable является ненужным шагом. Мой первый фрагмент кода случайно имел ссылку на удаленный  необязательный NodeWidget  (то есть, нет  ? ), Поэтому я не смог установить его на  ноль —  ошибка школьника! Теперь, когда  nodeWidgetPendingDelete  является необязательным, я могу установить его на  ноль,  не перепрыгивая через обручи!