Używanie języka Swift if let z operatorem logicznym AND &&

93

Wiemy, że możemy użyć if letinstrukcji jako skrótu, aby sprawdzić opcjonalne zero, a następnie rozpakować.

Jednak chcę to połączyć z innym wyrażeniem, używając operatora logicznego AND &&.

Na przykład tutaj wykonuję opcjonalne tworzenie łańcuchów, aby rozpakować i opcjonalnie obniżyć mój rootViewController do tabBarController. Ale zamiast zagnieżdżać instrukcje if, chciałbym je połączyć.

if let tabBarController = window!.rootViewController as? UITabBarController {
    if tabBarController.viewControllers.count > 0 {
        println("do stuff")
     }
 }

Dawanie łączone:

if let tabBarController = window!.rootViewController as? UITabBarController &&
    tabBarController.viewControllers.count > 0 {
        println("do stuff")
     }
}

Powyższe powoduje błąd kompilacji Użycie nierozwiązanego identyfikatora 'tabBarController'

Upraszczanie:

if let tabBarController = window!.rootViewController as? UITabBarController && true {
   println("do stuff")
}

Daje to błąd kompilacji Wartość powiązana w powiązaniu warunkowym musi być typu Opcjonalnego . Po próbie różnych wariacji składniowych każda daje inny błąd kompilatora. Nie znalazłem jeszcze zwycięskiej kombinacji kolejności i nawiasów.

Zatem pytanie brzmi, czy jest to możliwe, a jeśli tak, jaka jest poprawna składnia?

Zauważ, że chcę to zrobić z ifinstrukcją, a nieswitch instrukcją lub ?operatorem trójskładnikowym .

Max MacLeod
źródło
Zawsze dobrze jest podać komunikaty o błędach w pytaniu.
zaph
var foo : Int? = 10; if let bar = foo { if bar == 10 { println("Great success!") }}
Prostsze
Komunikat o błędzie podczas używania if let bar = foo && bar == 10to Use of unresolved identifier "bar"( baroczywiście po drugiej ).
DarkDust
tylko uwaga, że ​​udało mi się skrócić powyższy przykład za pomocą firstObject Objective C w następujący sposób: if let navigationController = tabBarController.viewControllers.bridgeToObjectiveC (). firstObject as? UINavigationController {
Max MacLeod
1
1. bridgeToObjectiveCzniknął w wersji beta 5 (i prawdopodobnie nigdy nie był przeznaczony do ogólnego użytku). 2. I tak jest firstmetoda wbudowanego Arraytypu Swift .
rickster

Odpowiedzi:

144

Od wersji Swift 1.2 jest to teraz możliwe . Informacje o wydaniu wersji beta Swift 1.2 i Xcode 6.3 zawierają :

Bardziej wydajne opcjonalne rozpakowywanie za pomocą if let - konstrukcja if let może teraz odpakowywać wiele opcji jednocześnie, a także zawierać interweniujące warunki boolowskie. Pozwala to wyrazić warunkowy przepływ sterowania bez niepotrzebnego zagnieżdżania.

Przy powyższym stwierdzeniu składnia byłaby wtedy następująca:

if let tabBarController = window!.rootViewController as? UITabBarController where tabBarController.viewControllers.count > 0 {
        println("do stuff")
}

Wykorzystuje whereklauzulę.

Inny przykład, tym razem rzutowanie AnyObjectna Int, rozpakowanie opcjonalnego i sprawdzenie, czy nieopakowany element opcjonalny spełnia warunek:

if let w = width as? Int where w < 500
{
    println("success!")
}

Dla tych, którzy teraz używają Swift 3, „gdzie” zostało zastąpione przecinkiem. Odpowiednik byłby zatem następujący:

if let w = width as? Int, w < 500
{
    println("success!")
}
Max MacLeod
źródło
2
To powinna nadejść wybrana odpowiedź.
Francis.Beauchamp
tak, ponieważ teraz się zmieniło. Odpowiedź Bryana była wówczas poprawna
Max MacLeod
59

W Swift 3 Max MacLeod przykład wyglądałby tak:

if let tabBarController = window!.rootViewController as? UITabBarController, tabBarController.viewControllers.count > 0 {
    println("do stuff")
}

whereZostał zastąpiony przez,

ph1lb4
źródło
10
Tak więc jest dla && do czego służy || ?
jeet.chanchawat
9
@ jeet.chanchawat logiczne OR nie ma sensu w tym kontekście. if letmożna kontynuować tylko wtedy, gdy opcjonalna pomyślnie rozpakowana i przypisana do nowej nazwy zmiennej. Jeśli tak powiedziałeś, OR <something else>możesz łatwo ominąć cały cel if let. Dla logicznego OR, po prostu wykonaj normalne ifi wewnątrz ciała zajmij się faktem, że pierwszy obiekt jest nadal opcjonalny w tym momencie (nie jest rozpakowany).
jhabbott
37

Odpowiedź Maxa jest poprawna i można to zrobić. Zauważ jednak, że napisane w ten sposób:

if let a = someOptional where someBool { }

someOptionalWyrażenie zostanie pierwszy rozwiązany. Jeśli to się nie powiedzie, pliksomeBool wyrażenie nie zostanie ocenione (ocena zwarcia, jak można się spodziewać).

Jeśli chcesz napisać to na odwrót, możesz to zrobić w następujący sposób:

if someBool, let a = someOptional { }

W tym przypadku someBooljest oceniany jako pierwszy i tylko wtedy, gdy zwraca wartość true, jest someOptionaloceniane wyrażenie.

par
źródło
5

Swift 4 , użyję,

let i = navigationController?.viewControllers.index(of: self)
if let index = i, index > 0, let parent = navigationController?.viewControllers[index-1] {
    // access parent
}
Sazzad Hissain Khan
źródło
4

To niemożliwe.

Od gramatyki Swift

GRAMATURA OŚWIADCZENIA IF

instrukcja-if → if jeśli warunek kodu blok else-clauseopt

warunek-jeśli → wyrażenie | deklaracja

klauzula-else → blok-kodu else | inaczej instrukcja-if

Wartość dowolnego warunku w instrukcji if musi mieć typ zgodny z protokołem BooleanType. Warunek może być również opcjonalną deklaracją powiązania, zgodnie z opisem w opcjonalnym powiązaniu

warunek-warunek musi być wyrażeniem lub deklaracją. Nie możesz mieć jednocześnie wyrażenia i deklaracji.

let foo = barjest deklaracją, nie daje wartości zgodnej z BooleanType. Deklaruje stałą / zmiennąfoo .

Twoje oryginalne rozwiązanie jest wystarczająco dobre, jest o wiele bardziej czytelne niż połączenie warunków.

Bryan Chen
źródło
ale z pewnością „if let” - co jest poprawną składnią języka Swift - jest zarówno wyrażeniem, jak i deklaracją?
Max MacLeod,
@MaxMacLeod let foo = barjest deklaracją, a nie wyrażeniem. nie możesz tego zrobićlet boolValue = (let foo = bar)
Bryan Chen
ok, ale cytując przewodnik po języku Swift, strona 11, przykładem jest: if let name = optionalName. nazwa_opcjonalna musi być wyrażeniem, przy czym jeśli nazwa_opcji nie jest opcjonalna, zwraca wartość true. Następnie rozpoczyna się druga część instrukcji, przez co nieopakowany element opcjonalny jest przypisywany do name. Zatem pytanie brzmi, czy jeśli „nazwa_opcjonalna nie jest opcjonalna” jest wyrażeniem, czy można ją rozszerzyć o logiczne AND. Btw myślę, że prawdopodobnie masz rację, ponieważ nie można tego zrobić.
Max MacLeod,
-1

Myślę, że twoja pierwotna propozycja nie jest taka zła. (Nieuporządkowaną) alternatywą byłoby:

if ((window!.rootViewController as? UITabBarController)?.viewControllers.count ?? 0) > 0 {
    println("do stuff")
}
jtbandes
źródło
1
ale musisz kodować, aby tabBarControllerponownie obsadzić
Bryan Chen,