2015-09-01 3 views
2

Следующий (рабочий) код создает UIAlertAction, который вызывает функцию foo() после нажатия кнопки ok.Swift Closures, которые ничего не принимают в качестве ввода

let action = UIAlertAction(title: "OK", style: .Default, handler: {action in self.foo()}) 

Мой вопрос: почему действие должно быть передано на закрытие?

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

{() in self.foo() } 

или

{ self in self.foo() } 

В C++ я бы ожидать, чтобы написать что-то вроде

[this](){this->foo();}; 

Что здесь происходит?

+1

Я считаю, что путаница вызвана тем, что имя «действие» в обработчике не обозначает «UIAlertAction», который вы определяете, это означает аргумент, который обработчик будет передан при его вызове. В C++ вы должны написать '[] (автоматическое действие) {this-> foo(); } ' – molbdnilo

ответ

3

Если компилятор может определить тип вашего закрытия, вы можете использовать неявное возвращение и сокращенные имена аргументов, тем самым объявив о закрытии, как:

let sorted = [ 4, 3, 1, 40, 19 ].sort { $0 > $1 } 

Однако, если компилятор не может определить его, вы необходимо объявить параметры, чтобы они могли быть уверены.

Возьмите этот пример, со своим собственным кодом:

class Bar { 

    func foo() { 
    } 

    func foo(s: String) { 
    } 

    func sample() { 
    // this works 
    let a = UIAlertAction(title: "a", style: .Default, handler: { self.foo($0.title) }) 
    // this doesn't work 
    let a = UIAlertAction(title: "a", style: .Default, handler: { self.foo() }) 
    } 
} 

Замыкание обработчик имеет определение ((UIAlertAction) -> (Пустота)!) !, так что ожидает закрытия с одним аргументом (предполагаемый тип UIAlertAction) и ничего не возвращает.

Первый пример работает, потому что, используя $ 0, вы говорите компилятору, что у закрытия есть фактически один аргумент, а поскольку последний вызов - это функция foo, которая ничего не возвращает, ваше закрытие имеет аргумент и ничего не возвращает, поэтому компилятор может использовать его таким образом.

Однако второй не работает, потому что даже если foo ничего не возвращает, вы не используете никаких параметров, и, таким образом, компилятор не может сказать, что у вашего закрытия есть аргумент. Для компилятора ваше закрытие: (Void) -> (Void), которое не является правильным типом.

Ответ ABakerSmith, вероятно, что вам нужно в этом случае, если ваша функция foo или определение закрытия не позволит определить тип замыкания компилятором. Вы все еще должны объявить параметры, но игнорировать их будет лучший курс действий:

let a = UIAlertAction(title: "a", style: .Default, handler: { _ in self.foo() }) 
+1

обработчик: {self.foo()} Это не будет компилироваться с помощью« Тип выражения неоднозначен без дополнительного контекста », следовательно, мой вопрос. (Xcode 7.0 beta 6, если это имеет значение) – aCuria

+0

См. Мой отредактированный ответ – ncerezo

2

Вы можете игнорировать аргументы, передаваемые закрытия обработчика с помощью подчеркивания, что делает ваш код:

{ _ in self.foo() } 

Вы можете использовать подчеркивание, чтобы игнорировать аргументы в других ситуациях тоже for циклы, например:

for _ in 0..<5 { 
    ... 
} 
+0

Ваш код компилируется и, вероятно, является правильным решением для этого случая, однако он не отвечает на вопрос «Почему мне нужно объявить аргумент закрытию "(поскольку вы все еще заявляете об этом, даже без имени). Я думаю, что мой ответ. Я отредактирую его, сделав ссылку на ваш, чтобы сделать его более полным, если вы не возражаете. – ncerezo

+0

Согласен, мой пост не полностью отвечает на вопрос OP (я был в спешке). Я не возражаю, хотя - я рад, что вы написали более полный ответ :) – ABakerSmith

1

@ ответ ncerzo является правильным - это просто clarif х годов. Как и все остальное в Swift, у закрытий есть тип, и компилятор не будет компилироваться, пока не сможет подтвердить, что тип закрытия, который вы передаете, соответствует типу, который требуется.Компилятор делает все возможное, чтобы вывести тип закрытия из контекста, но иногда он не может и требует, чтобы код был конкретным.

Вы говорите о закрытии, которое просто игнорирует переданный аргумент. Очень простой пример будет укупорочное который выглядит следующим образом:

let ignoreMe: Int -> Int = { _ in 42 } 

Независимо от того, какой аргумент вы передаете ignoreMe, она игнорируется, и возвращаемое значение будет 42. Поэтому вы, кажется, спрашиваете, почему подчеркивание необходимо вообще. Вы хотели бы написать вместо этого:

let ignoreMe: Int -> Int = { 42 } 

Проблема заключается в том, что компилятор не может соответствовать типу правой руки с типом левой стороны. Укупорочное средство { 42 } составляет : имеет тип Void -> Int, а в левой части ignoreMe - тип Int -> Int. Типы не совпадают.

Ошибка в способе компилятора сказать вам: «Я ожидал закрытия типа Int -> Int, но вы, похоже, дали мне закрытие типа Void -> Int. Пожалуйста, уточните свои намерения».

Подчеркивание в { _ in 42 } - это то, как вы предоставляете это разъяснение. Вы говорите компилятору: «Да, это закрытие будет принимать аргумент, но оно будет проигнорировано. Вы можете смело заключить, что его тип будет Int -> Int».

Требование об этом разъяснении может показаться ненужным багажом, но багаж светлый. { 42 } против { _ in 42 } не несет никакой нагрузки на кодер. И преимущества огромны. Для каждого преднамеренного несоответствие между левым типом и правым типом должно быть много непреднамеренно несоответствия (ака ошибки или ошибки). И эти непреднамеренные несоответствия должны быть исправлены до компиляции кода.