weak
ссылки, похоже, не работают в Swift, если protocol
не объявлен как @objc
, чего я не хочу в чистом приложении Swift.
Этот код дает ошибку компиляции (weak
не может применяться к типу некласса MyClassDelegate
):
class MyClass {
weak var delegate: MyClassDelegate?
}
protocol MyClassDelegate {
}
Мне нужно префикс протокола @objc
, затем он работает.
Вопрос: Что такое "чистый" быстрый способ выполнить weak
delegate
?
Вам нужно объявить тип протокола как class
.
protocol ProtocolNameDelegate: class {
// Protocol stuff goes here
}
class SomeClass {
weak var delegate: ProtocolNameDelegate?
}
Я понимаю, что с помощью class
вы гарантируете, что этот протокол будет использоваться только для классов и других вещей, таких как перечисления или структуры.
Меня всегда путали, должны ли делегаты быть слабыми или нет. Недавно я узнал больше о делегатах и когда использовать слабые ссылки, поэтому позвольте мне добавить некоторые дополнительные пункты здесь для будущих зрителей.
Цель использования ключевого слова weak
заключается в том, чтобы избежать сильных опорных циклов (сохранение циклов). Сильные циклы ссылок происходят, когда два экземпляра класса имеют сильные ссылки друг на друга. Их подсчеты ссылок никогда не обращаются к нулю, поэтому они никогда не освобождаются.
Вам нужно использовать weak
, если делегат является классом. Swift-структуры и перечисления представляют собой типы значений (их значения копируются при создании нового экземпляра), а не ссылочные типы, поэтому они не создают сильных ссылочных циклов.
weak
ссылки всегда являются необязательными (в противном случае вы использовали бы unowned
) и всегда используете var
(not let
), чтобы опционально можно было установить nil
при его освобождении.
Родительский класс должен, естественно, иметь сильную ссылку на его дочерние классы и, следовательно, не использовать ключевое слово weak
. Однако, когда ребенок хочет ссылку на своего родителя, он должен сделать его слабым, используя ключевое слово weak
.
weak
следует использовать, если вам нужна ссылка на класс, который у вас нет, а не только на ссылку на родителя. Когда два неиерархических класса должны ссылаться друг на друга, выберите один, чтобы быть слабым. Вы выбираете, зависит от ситуации. Подробнее см. Ответы на этот вопрос.
Как правило, делегаты должны быть помечены как weak
, потому что большинство делегатов ссылаются на классы, которым они не владеют. Это определенно верно, когда ребенок использует делегат для связи с родителем. Тем не менее, есть еще некоторые ситуации, где делегат может и должен использовать сильную ссылку.
Протоколы могут использоваться как для ссылочных типов (классов), так и для типов без ссылки (structs, enums). Поэтому в вероятном случае, когда вам нужно сделать делегат слабым, вам нужно добавить ключевое слово class
в протокол, чтобы он знал, что он должен использоваться только со ссылочными типами.
protocol MyClassDelegate: class {
// ...
}
class SomeClass {
weak var delegate: MyClassDelegate?
}
Чтение следующих статей помогло мне понять это намного лучше. Они также обсуждают связанные проблемы, такие как ключевое слово unowned
и сильные ссылочные циклы, которые происходят с закрытием.
Update: Похоже, что руководство было обновлено, и пример, о котором я говорил, был удален. См. Ответ на комментарий @flainez выше.
Оригинал: Использование @objc - это правильный способ сделать это, даже если вы не взаимодействуете с Obj-C. Он гарантирует, что ваш протокол применяется к классу, а не к перечислению или структуре. См. "Проверка соответствия протокола" в руководстве.
Apple использует "NSObjectProtocol" вместо "class".
public protocol UIScrollViewDelegate : NSObjectProtocol {
...
}
Это также работает для меня и удаляет ошибки, которые я видел при попытке реализовать собственный шаблон делегата.