2016-04-04 2 views
3

Кажется, что в синтаксисе списка захвата Swift наблюдается любопытный синтаксический сбой. Если я объявляю несколько захваченных переменных, захват спецификатор относится только к первому:Почему спецификатор захвата необязателен в списках захвата?

let closure = { [unowned x, y] in … } 

Теперь я хотел бы ожидать y быть unowned, но это, кажется, не так:

class Test { 

    var callback: (Void -> Void)? 

    init() { 
     print("init") 
    } 

    deinit { 
     print("deinit") 
    } 
} 

func makeScope() { 
    let x = Test() 
    let y = Test() 
    y.callback = { [unowned x, y] in 
     print(y) 
    } 
} 

makeScope() 
print("done") 

Это печатает:

init 
init 
deinit 
done 

Так y, кажется, сильно захвачен и создает сохранить цикл, предотвращая объект от того, сделка располагается. Это так? Если да, имеет ли смысл разрешить «пустой» спецификатор захвата в списке? Или есть причина, почему [unowned x, y] не рассматривается как [unowned x, unowned y]?

ответ

8

... имеет смысл разрешить «пустой» спецификатор захвата в списке?

Да, так оно и есть. спецификаторы захвата («слабый», «бесхозной» и его вариации) могут быть использованы только с ссылочных типов, но бывают и случаи, когда вы хотите, чтобы захватить тип в значение (здесь один пример: Pass value to closure?).

Вы также можете захотеть захватить ссылочный тип сильно. Захват ссылочного типа гарантирует, что ссылка (указатель) сам захвачена значением, как показано в следующем примере:

class MyClass { 
    let value : String 
    init(value : String) { 
     self.value = value 
    } 
} 

var ref = MyClass(value: "A") 

let clo1:() -> Void = { print(ref.value) } 
let clo2:() -> Void = { [ref] in print(ref.value) } 

ref = MyClass(value: "B") 

clo1() // Output: B 
clo2() // Output: A 

Когда первая крышка выполнена, ref внутри затвор является ссылкой на объект создан как MyClass(value: "B").

Второе закрытие фиксирует значение ref на момент создания закрытия, и это не изменится, если новое значение присваивается var ref.

+0

Хороший улов, спасибо. Считаете ли вы, что компилятор имеет смысл хотя бы выдать предупреждение, если спецификатор отсутствует перед ссылочным типом? – zoul

+0

@zoul: Нет, см. Пример выше. –

+0

Отлично, спасибо! Я думаю, что то, что делает меня несчастным, состоит в том, что простой, обычный случай «unowned x, y» выглядит ошибочным (ср.'int a, b' в C-подобных языках), в то время как редкий случай привязки ссылочного типа без указателя может быть легко (и более явно!) решен с использованием временной переменной. – zoul

1

Согласно syntax EBNF, этому лечение unownedзахвата спецификатора полностью намеренное:

затворной подписи → параметр придаточной-функция результат неавтоматическогоin­
закрытия подписи → идентификатора-лист функция-результат opt­in­
закрытие-подпись → параметр-параметр-список-список-функция неавтоматическогоin­
затворных подпись → снимаемый список идентификаторов, список функций, результат неавтоматического­in
закрытия подпись → Захват-лист in­
снимаемого список → [снимаемые элементы списка]
Capture- элементы списка → снимаемого элемента списка снимаемого элемента списка снимаемого элементы списка
снимаемого элемента списка → захвата спецификатора неавтоматического выражение
захват-спецификатор → weak | unowned | unowned(safe) | unowned(unsafe)­

Три линии в нижней части, определяющей <capture-list-items>, <capture-list-item> и <capture-specifier> производства являются наиболее актуальными здесь.

<capture-list-items> производства является разделенным запятыми списка <capture-list-item>, с capture-specifier прикреплен к каждому отдельным <capture-list-item>, чтобы не <capture-list-items> списка в целом.

Это имеет смысл, потому что дает программистам полный контроль над захватом отдельных аргументов. Альтернатива, когда спецификатор применим ко всему списку, уберет эту гибкость.

Почему бы вам не включить идентификатор в список захвата без изменения его спецификатора захвата?

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

+0

Ну, вопрос в том, почему нужно включить идентификатор в список захвата без изменения его спецификатора захвата? Но, как говорит Мартин, это действительно имеет смысл для типов значений. Тем не менее, поведение со ссылочными типами - это ошибка, ожидающая появления, ИМХО. – zoul

0

Чтобы ответить на конкретные вопросы:

Почему захват спецификатор необязательно в списках захвата?

Поскольку поведение по умолчанию заключается в захвате любых необходимых переменных (типы ссылок фиксируются сильно). По умолчанию вам не нужно указывать их явно в списке захвата, если вы хотите использовать их значения. (Хотя квалификации с self.property будет необходимо, если вы захвата self.)

... есть причина [unowned x, y] не рассматривается как [unowned x, unowned y]?

По той же причине: по умолчанию требуется фиксировать сильно. unowned не относится к другим позициям в списке захвата; это просто не то, как синтаксис работает прямо сейчас.

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