Как защитить существующее назначение переменных без let/var в Swift 2

Ключевое слово guard великолепно, и я использую его с тех пор, как вышел Swift 2. Я смог успешно использовать его во многих сценариях, включая этот:

for viewController in self.navigationController!.viewControllers {
    guard let myCustomVC = viewController as? MyCustomViewController else {continue}

    myCustomVC.doWhatItHasToDo()
    break
}

Работает как шарм. Перебирает все viewControllers в текущем navigationController, пока не найдет тот, который мне нужен.

Проблема в следующем: теперь мне нужно обновить приведенный выше код, чтобы повторно использовать переменную myCustomVC за пределами области for..in. Это должно быть так же просто, как объявить переменную перед областью действия, но я не могу найти способ guard присвоения переменной без ключевых слов let или var.

Следующий гипотетический код демонстрирует, чего я пытаюсь достичь, но Swift не позволяет мне этого сделать:

var myCustomVC: MyCustomViewController
for viewController in self.navigationController!.viewControllers {
    guard myCustomVC = viewController as? MyCustomViewController else {continue}

    myCustomVC.doWhatItHasToDo()
    break
}

// Now myCustomVC is available outside the for...in scope
myCustomVC.doWhatever()

Xcode предлагает использовать == вместо =, так как ожидает, что оператор guard будет логическим контекстом, а не назначением переменной.

Ошибка Xcode

Любые идеи о том, как обойти эту проблему?


person mathielo    schedule 18.12.2015    source источник
comment
Можете ли вы просто использовать фильтр, чтобы найти нужный контроллер?   -  person Michael Welch    schedule 19.12.2015
comment
let myCustomVC = viewControllers.filter { /* поместите здесь предикат */ }.first; myCustomVC.doWhatItHasToDo(); myCustomVC.doWhatever();   -  person Michael Welch    schedule 19.12.2015


Ответы (2)


Извините, что не ответил на ваш вопрос. Я не думаю, что то, что вы просите, возможно. Но у меня есть альтернатива.

Похоже, вы вручную пишете метод filter снова и снова. Я бы предложил просто использовать filter, чтобы найти нужный элемент (элементы). Если совпадений несколько, используйте first, чтобы выбрать первое. Например, ниже я выбираю первое четное число из списка:

let myArray = [0,1,3,5,7,4,6,10,11]
let myNum = myArray.filter { $0 % 2 == 0 }.first

Я не знаю точного синтаксиса для вашего предиката, но что-то вроде следующего

let myCustomVC = self.navigationController!.viewControllers.filter {
    $0 is MyCustomViewController }.first

// myCustomVC will be an optional because first returns an optional
if let myCustomVC = myCustomVC {
    myCustomVC.doWhatItHasToDo()
    myCustomVC.doWhatever()
}

Или, если у вас есть действие, которое нужно выполнить для каждого элемента, соответствующего предикату, используйте foreach

    let myCustomVC = self.navigationController!.viewControllers.filter {
    $0 is MyCustomViewController }.foreach { // action // }
person Michael Welch    schedule 18.12.2015
comment
Супер ответ Михаил! Спасибо за подробное объяснение и замечательные примеры. На самом деле я изобретал колесо, сам того не осознавая. - person mathielo; 19.12.2015
comment
Прохладный. Рад, что это соответствует вашим потребностям - person Michael Welch; 19.12.2015

Я продолжал думать над твоим вопросом. Можно сделать то, что вы изначально просили, однако решение, которое я придумал, немного запутано. По сути, это включает в себя определение замыкания типа Any -> Bool, которое вы можете использовать в своем операторе защиты. Вот полный пример. Возможно, если это будет полезно, вы могли бы его немного почистить. Фактический цикл for неплох, если предположить, что вы уже где-то определили замыкание предиката.

class MyClass {}

var myCollection = [Any]()
// add sample elements 
myCollection.append("hello")
myCollection.append(MyClass())

// declare the variable to receive the "found" element
var myElement:MyClass? = nil

// define our predicate to use in guard statement
let myPredicate:Any->Bool = { obj in
    if let obj = obj as? MyClass {
        myElement = obj
        return true
    }
    return false
}

for v in myCollection {
    guard myPredicate(v)
    else {continue}

   // do something with v
}

if let myElement = myElement {
    print(myElement)
}

ОБНОВЛЕНИЕ: гораздо более простой пример (он все еще требует разрешения)

for v in myCollection {
    guard let myObj = v as? MyClass else {continue}
    myElement = myObj
}
person Michael Welch    schedule 19.12.2015