Как сделать слабую ссылку на протокол в "чистом" Swift (без @objc)
weak
ссылки не работают в Swift, если только protocol
объявлен как @objc
, который я не хочу в чистом приложении Swift.
Этот код выдает ошибку компиляции (weak
не может быть применено к неклассному типу MyClassDelegate
):
class MyClass {
weak var delegate: MyClassDelegate?
}
protocol MyClassDelegate {
}
Мне нужно префикс протокола с @objc
тогда это работает.
Вопрос: что такое "чистый" способ Свифта weak
delegate
?
8 ответов
Вы должны объявить тип протокола как class
,
protocol ProtocolNameDelegate: class {
// Protocol stuff goes here
}
class SomeClass {
weak var delegate: ProtocolNameDelegate?
}
Я понимаю, что с помощью class
, вы гарантируете, что этот протокол будет использоваться только на классах, а не на других вещах, таких как перечисления или структуры.
Дополнительный ответ
Меня всегда смущало, должны ли делегаты быть слабыми или нет. Недавно я узнал больше о делегатах и о том, когда использовать слабые ссылки, поэтому позвольте мне добавить некоторые дополнительные замечания здесь для будущих зрителей.
Цель использования
weak
Ключевое слово заключается в том, чтобы избежать сильных ссылочных циклов (сохранить циклы). Сильные ссылочные циклы происходят, когда два экземпляра класса имеют сильные ссылки друг на друга. Их счетчик ссылок никогда не обнуляется, поэтому их никогда не освобождают.Вам нужно только использовать
weak
если делегат является классом. Структуры и перечисления Swift являются типами значений (их значения копируются при создании нового экземпляра), а не ссылочными типами, поэтому они не создают циклы сильных ссылок.weak
ссылки всегда необязательны (в противном случае вы бы использовалиunowned
) и всегда используюvar
(неlet
) так что необязательный может быть установлен вnil
когда он освобожден.Естественно, родительский класс должен иметь строгую ссылку на свои дочерние классы и, следовательно, не использовать
weak
ключевое слово. Когда ребенок хочет ссылку на своего родителя, он должен сделать это слабой ссылкой, используяweak
ключевое слово.weak
должен использоваться, когда вы хотите ссылку на класс, который вам не принадлежит, а не только на ребенка, ссылающегося на его родителя. Когда два неиерархических класса должны ссылаться друг на друга, выберите один, чтобы быть слабым. Выбор зависит от ситуации. Смотрите ответы на этот вопрос для получения дополнительной информации по этому вопросу.Как правило, делегаты должны быть помечены как
weak
потому что большинство делегатов ссылаются на классы, которые им не принадлежат. Это определенно верно, когда ребенок использует делегата для общения с родителем. Однако все еще существуют ситуации, когда делегат может и должен использовать строгую ссылку.Протоколы могут использоваться как для ссылочных типов (классов), так и для типов значений (структуры, перечисления). Таким образом, в вероятном случае, когда вам нужно сделать делегата слабым, вы должны добавить
class
ключевое слово протокола, чтобы он знал, что он должен использоваться только со ссылочными типами.protocol MyClassDelegate: class { // ... } class SomeClass { weak var delegate: MyClassDelegate? }
Дальнейшее обучение
Чтение следующих статей помогло мне понять это намного лучше. Они также обсуждают связанные вопросы, такие как unowned
ключевое слово и циклы сильных ссылок, которые происходят с замыканиями.
- Документация Swift: Автоматический подсчет ссылок
- "Слабый, сильный, неизвестный, о мой!" - Справочник по ссылкам в Swift
- Сильный, слабый и незнакомый - сортировка ARC и Swift
связанные с
AnyObject
это официальный способ использовать слабую ссылку в Swift.
class MyClass {
weak var delegate: MyClassDelegate?
}
protocol MyClassDelegate: AnyObject {
}
От Apple:
Чтобы предотвратить циклы сильных ссылок, делегаты должны быть объявлены как слабые ссылки. Для получения дополнительной информации о слабых ссылках см. Сильные циклы ссылок между экземплярами классов. Пометка протокола как класса только позже позволит вам объявить, что делегат должен использовать слабую ссылку. Вы помечаете протокол как только для класса, наследуя от AnyObject, как обсуждалось в Протоколах только для класса.
Обновление: похоже, руководство было обновлено, а пример, на который я ссылался, был удален. Смотрите изменение ответа @flainez выше.
Оригинал: использование @objc - верный способ сделать это, даже если вы не взаимодействуете с Obj-C. Это гарантирует, что ваш протокол применяется к классу, а не к перечислению или структуре. См. "Проверка соответствия протокола" в руководстве.
Квалификатор weak применяется только к ссылочным объектам. Если вы не добавите в свой протокол квалификатор, или, объект, соответствующий протоколу, может не быть ссылочным объектом.
Таким образом, вам понадобится один из этих квалификаторов (и
AnyObject
рекомендуется, так как
class
считается устаревшим.)
Кстати, обратите внимание, что добавление
@objc
к вашим классам и свойствам иногда требуется, даже в приложениях "на чистом Swift". Это не имеет отношения к вашему языку разработки. Это заставляет компилятор строить ваш код таким образом, чтобы он был совместим со средой выполнения Objective-C, которая требуется для некоторых интерфейсов ОС (например, путь к цели / действию и ключевые пути старого стиля)
protocol MyProtocol {
func doSomething()
}
class MyClass: MyProtocol {
func doSomething() {
print("Doing something")
}
}
var weakProtocol: Weak<MyProtocol>?
let myObject = MyClass()
weakProtocol = Weak(myObject)
weakProtocol?.doSomething() // Will print "Doing something"
протокол должен быть подклассом AnyObject, класса
пример приведен ниже
protocol NameOfProtocol: class {
// member of protocol
}
class ClassName: UIViewController {
weak var delegate: NameOfProtocol?
}
Apple использует "NSObjectProtocol" вместо "class".
public protocol UIScrollViewDelegate : NSObjectProtocol {
...
}
Это также работает для меня и устраняет ошибки, которые я видел при попытке реализовать свой собственный шаблон делегата.