2014-12-06 2 views
9

В частности, как работает управление памятью Swift с опциями с использованием шаблона делегата?Как работает управление памятью Swift?

Будучи привыкшим к написанию шаблона делегата в Objective-C, мой инстинкт состоит в том, чтобы сделать делегата weak. Например, в Objective-C:

@property (weak) id<FooDelegate> delegate; 

Однако выполнение этого в Swift не так прямолинейно.

Если у нас есть только обычный выглядящий протокол:

protocol FooDelegate { 
    func doStuff() 
} 

Мы не можем объявлять переменные этого типа, как слабая:

weak var delegate: FooDelegate? 

Выдает ошибку:

'weak' cannot be applied to non-class type 'FooDelegate'

Таким образом, мы либо не используйте ключевое слово weak, что позволяет нам использовать structs и enums в качестве делегатов, или мы изменим наш протокол к следующему:

protocol FooDelegate: class { 
    func doStuff() 
} 

что позволяет использовать weak, но не позволяет нам использовать structs или enums.

Если я не делаю свой протокол протоколом класса и поэтому не использую weak для моей переменной, я создаю цикл сохранения, правильно?

Существует ли какая-либо мыслимая причина, почему любой протокол, предназначенный для использования в качестве протокола делегата, не должен быть протоколом класса, так что переменные этого типа могут быть weak?

я в первую очередь спрашиваю, потому что в разделе делегации the Apple official documentation on Swift protocols, они представляют собой пример протокола неклассового и без слабого переменного, используемого в качестве делегата в свой класс:

protocol DiceGameDelegate { 
    func gameDidStart(game: DiceGame) 
    func game(game: DiceGame, didStartNewTurnWithDiceRoll diceRoll: Int) 
    func gameDidEnd(game: DiceGame) 
} 
class SnakesAndLadders: DiceGame { 
    let finalSquare = 25 
    let dice = Dice(sides: 6, generator: LinearCongruentialGenerator()) 
    var square = 0 
    var board: [Int] 
    init() { 
     board = [Int](count: finalSquare + 1, repeatedValue: 0) 
     board[03] = +08; board[06] = +11; board[09] = +09; board[10] = +02 
     board[14] = -10; board[19] = -11; board[22] = -02; board[24] = -08 
    } 
    var delegate: DiceGameDelegate? 
    func play() { 
     square = 0 
     delegate?.gameDidStart(self) 
     gameLoop: while square != finalSquare { 
      let diceRoll = dice.roll() 
      delegate?.game(self, didStartNewTurnWithDiceRoll: diceRoll) 
      switch square + diceRoll { 
      case finalSquare: 
       break gameLoop 
      case let newSquare where newSquare > finalSquare: 
       continue gameLoop 
      default: 
       square += diceRoll 
       square += board[square] 
      } 
     } 
     delegate?.gameDidEnd(self) 
    } 
} 

Должны ли мы считать это как подсказку, что Apple думает, что мы должны использовать структуры как делегаты? Или это просто плохой пример, и реалистично, протоколы делегатов должны быть объявлены как протоколы только для классов, чтобы делегированный объект мог иметь слабую ссылку на его делегата?

+0

«Если я не сделаю протокол протоколом класса и поэтому не использую слабый для моей переменной, я создаю цикл сохранения, правильно?» Нет, не правильно. Если делегат не сохранит ссылку на своего хозяина. В примере Swift этого не происходит. DiceGameDelegate имеет параметры DiceGame в своих функциях, но у него нет DiceGame _property_, что вызовет постоянную ссылку на его хост. – matt

+0

Но в большинстве случаев фактического использования делегат имеет ссылку на делегированный объект. – nhgrif

+0

Правильно, и это именно то, о чем говорит мой ответ. - В основном вы спросили, что особенного в этом примере Apple, который использует _not_ класс и слабый делегат, и я сказал вам. – matt

ответ

7

Should we take this as a hint that Apple thinks we should be using structs as delegates? Or is this simply a bad example, and realistically, delegate protocols should be declared as class-only protocols so that the delegated object can hold a weak reference to its delegate?

Вот что. В реальной жизни программирования Cocoa, делегат, вероятно, будет существующим классом. Это класс, потому что он существует для какой-то другой цели, которую может удовлетворить только класс - потому что Какао требует его.

Например, очень часто, чтобы взять iOS в качестве примера, один контроллер представлений должен действовать в качестве другого делегата диспетчера представления для целей организации сообщения между ними. Владение контроллерами представлений диктуется иерархией диспетчера представлений и ничем иным. Итак, в Swift, как и в Objective-C, у вас было лучше сделать это delegate property weak, потому что было бы ужасно, если бы один диспетчер представлений вдруг взял управление памятью другого контроллера вида!

Таким образом, в реальном мире каркаса Cocoa существует серьезная опасность неправильного владения или цикла удержания. И это проблема, которую решает weak. Но это работает, как вы справедливо говорите, с классами.

Пример в книге, однако, о некоторых объектах, живущих в абстрактном искусственном мире, созданном исключительно для Swift. В этом мире, пока вы не подвергаетесь опасности округлости (сохраняете цикл), нет причин не использовать структуры, и нет причин беспокоиться об управлении памятью. Но этот мир - это не тот мир, в который вы, как правило, программируете! И этот мир не является каркасом Cocoa, в который входит и принадлежит ваш объект-объект Objective-C.

+0

Этот вопрос не отмечен iOS, и хотя я сделал очень мало OS X, я уверен, что видел подкласс NSArray, используемый как «NSTableDataSource». Не кажется ли мне, что я могу использовать массив (который является структурой в Swift) как делегат типа источника данных? – nhgrif

+2

Swift array - это структура, но она привязана к NSArray (классу). - Однако в ответ на ваш вопрос нет: в каком возможном сценарии массив будет источником данных? Источники данных - это вещи, которые могут отвечать на вопросы протокола источника данных. У вас никогда не будет такого массива. – matt

+0

@nhgrif: Использование 'NSArray' * as * источника данных таблицы (в OS X или iOS) - действительно плохая идея. Это означало бы расширение класса NSArray для реализации методов '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '' '', Вероятно, вы видели * classes *, которые реализуют методы источника данных табличного представления *, используя * массив. Аргумент Мэтта, по крайней мере, для Cocoa: места, где Cocoa (Touch) хочет, чтобы делегат был местом, требующим тип класса, и, вероятно, будет продолжаться. – rickster

2

Да, этот пример немного странен.

Поскольку в примере используется тип протокола, отличного от класса, он должен ожидать возможную структуру реализации протокола, что означает, что экземпляр DiceGame владеет своим делегатом. И действительно, это нарушает типичные предположения о шаблоне делегата.

Это не приводит к исходному циклу в этом случае потому что DiceGameTracker вымышленный объект, который не является владельцем самого DiceGame - но в реальном мире приложение, это возможно, даже вероятно, что делегат также может быть владельцем делегирующего объекта. (Например, контроллер представления может владеть DiceGame и реализовать DiceGameDelegate, чтобы он мог обновлять свой интерфейс в ответ на игровые события.)

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

Реально можно было бы использовать ссылочные типы (классы) для реализации этого в любом случае, даже если объявление протокола оставляет открытым возможность сделать это иначе. Даже в гипотетическом мире Swift, вероятно, имеет смысл сделать это таким образом ... обычно, когда у вас есть что-то с долгой жизнью, внутренним изменчивым состоянием и шаблоном использования, к которому он обращается потенциально несколькими другими актерами, вы хотите тип класса, даже если вы можете сортировать поддельные в противном случае с типами значений и var.

2

Если у вас есть structs и emums в вашем протоколе, то если вы сделаете свой делегат nil как раз перед закрытием контроллера вида, это нарушит цикл сохранения. Я проверил это в «Выделениях в Инструментах».

// view controller about to close 
objectCreatedByMe.delegate = nil 

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

+0

Должно быть очевидно, что это нарушает цикл. Но есть много причин, почему это не очень хорошо ... – nhgrif

+0

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

+1

Я проголосовал за вас. Вы пытались ответить, вы не поместили ничего плохого, что я могу сказать. И если бы это было неправильно, через неделю или два Apple изменит язык, и вы, вероятно, будете правы. Быстро движется быстро. Я не поддаваюсь никому, пока он не выйдет на плато. Черт, они просто добавили 'Set' на прошлой неделе. – Darrell

Смежные вопросы