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

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

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

class MyClass {
  weak var delegate: MyClassDelegate?
}

protocol MyClassDelegate {
}

Мне нужно префикс протокола с @objcтогда это работает.

Вопрос: что такое "чистый" способ Свифта weakdelegate?

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 ключевое слово и циклы сильных ссылок, которые происходят с замыканиями.

связанные с

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

Обновление: похоже, руководство было обновлено, а пример, на который я ссылался, был удален. Смотрите изменение ответа @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 {
   ...
}

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

Другие вопросы по тегам