Как сделать слабую ссылку на протокол в "чистом" Swift (без @objc)

weak ссылки, похоже, не работают в Swift, если protocol не объявлен как @objc, чего я не хочу в чистом приложении Swift.

Этот код дает ошибку компиляции (weak не может применяться к типу некласса MyClassDelegate):

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

Мне нужно префикс протокола @objc, затем он работает.

Вопрос: Что такое "чистый" быстрый способ выполнить weak delegate?

Ответы

Ответ 1

Вам нужно объявить тип протокола как class.

protocol ProtocolNameDelegate: class {
    // Protocol stuff goes here
}

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

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

Ответ 2

Дополнительный ответ

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

  • Цель использования weak ключевого слова - избежать циклов сильных ссылок (сохранить циклы). Сильные ссылочные циклы происходят, когда два экземпляра класса имеют сильные ссылки друг на друга. Их счетчик ссылок никогда не обнуляется, поэтому их никогда не освобождают.

  • Вам нужно использовать weak если делегат является классом. Структуры и перечисления Swift являются типами значений (их значения копируются при создании нового экземпляра), а не ссылочными типами, поэтому они не создают сильных ссылочных циклов.

  • weak ссылки всегда являются необязательными (в противном случае вы бы использовали unowned) и всегда используете var (not let), так что необязательный параметр может быть установлен равным nil при его освобождении.

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

  • weak должен использоваться, когда вы хотите ссылку на класс, который вам не принадлежит, а не только для ребенка, ссылающегося на его родителя. Когда два неиерархических класса должны ссылаться друг на друга, выберите один, чтобы быть слабым. Выбор зависит от ситуации. Смотрите ответы на этот вопрос для получения дополнительной информации по этому вопросу.

  • Как правило, делегаты должны быть помечены как weak поскольку большинство делегатов ссылаются на классы, которыми они не владеют. Это определенно верно, когда ребенок использует делегата для общения с родителем. Использование слабой ссылки для делегата - это то, что рекомендует документация. (Но посмотрите и это тоже.)

  • Протоколы могут использоваться как для ссылочных типов (классов), так и для типов значений (структуры, перечисления). Таким образом, в вероятном случае, когда вам нужно сделать делегата слабым, вы должны сделать его протоколом только для объекта. Способ сделать это - добавить AnyObject в список наследования протокола. (В прошлом вы делали это с помощью ключевого слова class, но AnyObject предпочтительным является AnyObject.)

    protocol MyClassDelegate: AnyObject {
        // ...
    }
    
    class SomeClass {
        weak var delegate: MyClassDelegate?
    }
    

Дальнейшее обучение

Чтение следующих статей помогло мне понять это намного лучше. Они также обсуждают связанные с этим вопросы, такие как unowned ключевое слово и циклы сильных ссылок, которые происходят с замыканиями.

связанные с

Ответ 3

AnyObject - это официальный способ использовать слабую ссылку в Swift.

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

От Apple:

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

https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/Protocols.html#//apple_ref/doc/uid/TP40014097-CH25-ID276

Ответ 4

Update: Похоже, что руководство было обновлено, и пример, о котором я говорил, был удален. См. Ответ на комментарий @flainez выше.

Оригинал: Использование @objc - это правильный способ сделать это, даже если вы не взаимодействуете с Obj-C. Он гарантирует, что ваш протокол применяется к классу, а не к перечислению или структуре. См. "Проверка соответствия протокола" в руководстве.

Ответ 5

протокол должен быть подклассом AnyObject, класса

пример приведен ниже

    protocol NameOfProtocol: class {
   // member of protocol
    }
   class ClassName: UIViewController {
      weak var delegate: NameOfProtocol? 
    }

Ответ 6

Apple использует "NSObjectProtocol" вместо "class".

public protocol UIScrollViewDelegate : NSObjectProtocol {
   ...
}

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