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


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

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

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

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

вопрос: каков "чистый" быстрый способ выполнить weakdelegate?

5 462

5 ответов:

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

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

class SomeClass {
    weak var delegate: ProtocolNameDelegate?
}

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

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

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

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

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

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

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

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

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

  • протоколы могут быть использованы как для ссылка типа (классов) и типы значений (структуры, перечисления). Поэтому в вероятном случае, когда вам нужно сделать делегат слабым, вы должны добавить class ключевое слово для протокола, чтобы он знал, что он должен использоваться только со ссылочными типами.

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

далее Исследование

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

по теме

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

class MyClass {
    weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate: AnyObject {
}

От Apple:

чтобы предотвратить сильные ссылочные циклы, делегаты должны быть объявлены как слабая ссылка. Дополнительные сведения о слабых ссылках см. В разделе Сильные Ссылочные Циклы Между Экземплярами Класса. Маркировка протокола as class-only позже позволит вам объявить, что делегат должен использовать слабую ссылку. Вы отмечаете протокол как класс-только наследование от какой-либо объект, как описано в протоколах только для класса.

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

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

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

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

public protocol UIScrollViewDelegate : NSObjectProtocol {
   ...
}

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