Метод вызова с использованием необязательной цепочки на слабой переменной вызывает 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()