Метод вызова с использованием необязательной цепочки на слабой переменной вызывает EXC_BAD_ACCESS

Обновление:. Это исправлено в Xcode 6 beta 6.

Следующий код вызывает EXC_BAD_ACCESS в строке delegate?.thing():

@class_protocol protocol Fooable {
  func foo()
}

class Bar : Fooable {
  func foo() {
  }
}

weak var delegate: Fooable?

let bar = Bar()
delegate = bar
delegate?.foo()

Но все кажется мне правильным. Для того чтобы переменная была weak, она должна иметь необязательный тип. Таким образом, переменная delegate является необязательной. Слабый тип переменной также должен быть типом класса, поэтому я сделал протокол протоколом класса. Поскольку я использую необязательную цепочку, я ожидаю, что она будет 1) быть nil и ничего не делать, или 2) не быть nil, и вызвать метод, который должен быть успешным. Однако он сбой.

Может ли быть, что необязательная цепочка не является атомарной и не сохраняет выражение, и объект каким-то образом освобождается между проверкой на nil и последующим вызовом?

Интересно, что если вы исключите переменную bar и назначьте ее непосредственно как delegate = Bar(), авария исчезнет. Это действительно озадачивает, потому что назначение выражения переменной и последующее назначение переменной и назначение выражения напрямую должны вести себя одинаково.

Ответы

Ответ 1

Я подозреваю, что причина weak var delegate: Fooable? не работает, потому что эта строка кода, которая использует необязательную цепочку, проверяет соответствие протокола.

Согласно Руководству по программированию Apple Swift:

"Даже если вы не взаимодействуете с Objective-C, вам нужно отметить ваши протоколы с атрибутом @objc, если вы хотите иметь возможность проверьте соответствие протокола.

Если вы замените @class_protocol на @objc, он не должен сбой. Кроме того, согласно руководству, использование @objc позволяет только принимать протокол по классам (нет структур или соответствия перечислений).

Ответ 2

Как и @PartiallyFinite, мне тоже пришлось играть с кодом. Apple iBook on Swift имеет рекламу, которая может помочь (с протоколом с именем ExampleProtocol и классом SimpleClass, который соответствует этому протоколу): "Несмотря на то, что переменная protocolValue имеет тип времени выполнения SimpleClass, компилятор рассматривает его как заданный тип ExampleProtocol. Это означает, что вы не можете случайно получить доступ к методам или свойствам, которые класс реализует в дополнение к его совместимости с протоколом".

Тем не менее, вы должны иметь возможность вызывать foo() на своем делетете (запрет не уходит, прежде чем вызывать его), но просто помните, что делегат здесь объявлен как тип Fooable?, а не Bar, под капот. Это может быть ошибка, но я получил ее для работы, введя:

weak var delegate: Bar? = bar
delegate?.foo()