Swift: Nil için test seçenekleri


137

Xcode 6 Beta 4 kullanıyorum. Seçeneklerin nasıl uygun bir şekilde test edileceğini bulamadığım bu garip durum var.

İsteğe bağlı bir xyz varsa, test etmek için doğru yoldur:

if (xyz) // Do something

veya

if (xyz != nil) // Do something

Belgeler bunu ilk şekilde yapmayı söylüyor, ancak bazen ikinci yolun gerekli olduğunu ve derleyici hatası üretmediğini, ancak diğer zamanlarda ikinci yolun bir derleyici hatası oluşturduğunu gördüm.

Özel örneğim, hızlıca köprülenmiş GData XML ayrıştırıcısını kullanmaktır:

let xml = GDataXMLDocument(
    XMLString: responseBody,
    options: 0,
    error: &xmlError);

if (xmlError != nil)

Burada, eğer yaptım:

if xmlError

her zaman doğru dönecekti. Ancak, eğer:

if (xmlError != nil)

sonra çalışır (Objective-C'de nasıl çalıştığı gibi).

GData XML ve eksik olan opsiyonel tedavi şekli ile ilgili bir şey var mı?


4
Beklenmedik durumunuz ve hata durumlarınız için tam bir örnek görebilir miyiz lütfen? (Ve zor olduğunu biliyorum, ancak koşulların etrafındaki parantezleri kaybetmeye başlamaya çalışın!)
Matt Gibson

1
bu Xcode 6 beta 5'te değiştirildi
newacct


Soruyu beklenmedik durumla güncelledim.
tng

Henüz beta 5'e güncelleme yapmadım. Bunu yakında yapacağım; gördüğüme sevindim ?? Swift ve daha tutarlı isteğe bağlı davranış.
tng

Yanıtlar:


172

Xcode Beta 5'te artık şunları yapmanıza izin vermiyorlar:

var xyz : NSString?

if xyz {
  // Do something using `xyz`.
}

Bu bir hata üretir:

'BooleanType.Protocol' protokolüne uymuyor

Bu formlardan birini kullanmanız gerekir:

if xyz != nil {
   // Do something using `xyz`.
}

if let xy = xyz {
   // Do something using `xy`.
}

5
Birisi neden xyz == nil'in çalışmadığı hakkında ayrıntılı bilgi verebilir mi?
Christoph

İkinci örnek şöyle olmalıdır: // xy kullanarak bir şeyler yapın (iki değişken arasındaki kullanım durumlarındaki temel farktır)
Alper

@Christoph: Hata mesajı, initalize edilmeden önce kullanılan sabit 'xyz'. Bence xyz bildirim satırının sonuna '= nil' eklemeniz gerekiyor.
user3207158

Benim gibi hızlı yeni olanlar ve merak meraklıları için: Hala if blok içinde xyz paketini açmak zorunda.
Omar

@Omar İkinci formun güzelliği, bloğun içinde xy'yi açmak zorunda kalmadan kullanıyorsunuz.
Erik Doernenburg

24

Bir ifkoşulun içinde farklı adlandırılmış bir değişkene atamak yerine diğer yanıtlara eklemek için :

var a: Int? = 5

if let b = a {
   // do something
}

aynı değişken adını şu şekilde tekrar kullanabilirsiniz:

var a: Int? = 5

if let a = a {
    // do something
}

Bu, reklam değişkeni adlarının tükenmesini önlemenize yardımcı olabilir ...

Bu, Swift'te desteklenen değişken gölgelemeden yararlanır .


18

Opsiyonelleri kullanmanın en doğrudan yollarından biri şudur:

Örneğin xyz, isteğe bağlı tipte olduğu varsayılır Int?.

if let possXYZ = xyz {
    // do something with possXYZ (the unwrapped value of xyz)
} else {
    // do something now that we know xyz is .None
}

Bu şekilde xyz, bir değer içerip içermediğini test edebilir ve varsa hemen bu değerle çalışabilirsiniz.

Derleyici hatanızla ilgili olarak, tür UInt8isteğe bağlı değildir (not no '?') Ve bu nedenle dönüştürülemez nil. Bir değişken gibi davranmadan önce üzerinde çalıştığınız değişkenin isteğe bağlı olduğundan emin olun.


1
Bu yöntemi kötü buluyorum çünkü gereksiz yere yeni bir değişken (sabit) oluşturuyorum ve çoğu zaman öncekinden daha kötü olacak yeni bir isim oluşturmak zorunda kalıyorum. Yazmak ve okuyucu için daha fazla zaman harcıyor.
Lombas

3
İsteğe bağlı bir değişkenin paketini açmak için standart yöntem ve önerilen yöntemdir. "'let let" çok güçlü.
gnasher729

@ gnasher729 ama sadece başka bir parçayı kullanmak istiyorsanız
Brad Thomas

15

Swift 3.0, 4.0

Nil için isteğe bağlı kontrol etmenin başlıca iki yolu vardır. İşte aralarında karşılaştırmalı örnekler

1. eğer izin

if letNil için isteğe bağlı kontrol etmenin en temel yoludur. Bu sıfır kontrolüne virgülle ayrılmış başka koşullar eklenebilir. Değişken bir sonraki koşul için hareket ettirilemez. Yalnızca sıfır kontrolü gerekiyorsa, aşağıdaki koddaki ek koşulları kaldırın.

Bunun dışında, xnil değilse, if kapatma gerçekleştirilecek ve x_valiçeride bulunacaktır. Aksi takdirde, diğer kapatma tetiklenir.

if let x_val = x, x_val > 5 {
    //x_val available on this scope
} else {

}

2. bekçi izin

guard letbenzer şeyler yapabilir. Temel amacı mantıklı olarak daha makul hale getirmektir. Bu , değişkenin sıfır olmadığından emin olun, aksi takdirde işlevi durdurun demek gibidir . guard letayrıca ekstra durum kontrolü yapabilirsiniz if let.

Farklılıklar, kaydırılmamış değerin guard letaşağıdaki yorumda gösterildiği gibi aynı kapsamda kullanılabilmesidir . Bu aynı zamanda tarafından, başka kapanmasında program geçerli kapsamı çıkmak zorunda olduğu noktaya götürür return, breakvb

guard let x_val = x, x_val > 5 else {
    return
}
//x_val available on this scope

11

Hızlı programlama kılavuzundan

If İfadeleri ve Zorla Açma

İsteğe bağlı bir değer içerip içermediğini öğrenmek için bir if ifadesi kullanabilirsiniz. İsteğe bağlı bir değere sahipse true olarak değerlendirilir; hiç değeri yoksa, yanlış olarak değerlendirilir.

Bunu yapmanın en iyi yolu

// swift > 3
if xyz != nil {}

ve eğer if deyimini kullanıyorsanız. Sabit deyimde if deyimini xyzaçabilirsiniz. xyzYani if ​​deyiminin xyzkullanıldığı yerde her yeri açmanız gerekmez .

if let yourConstant = xyz{
      //use youtConstant you do not need to unwrap `xyz`
}

Bu konvansiyon önerilmektedir appleve arkasından geliştiriciler gelecektir.


2
Swift 3'te yapmanız gerekenler if xyz != nil {...}.
psmythirl

Swift 3'te opsiyoneller boole olarak kullanılamaz. Birincisi, ilk etapta asla dilde olmaması gereken bir C-izmidir ve ikincisi, isteğe bağlı Bool ile mutlak karışıklığa neden olur.
gnasher729

9

Her ne kadar açıkça bir isteğe bağlı ile nildeğerini karşılaştırmalı veya değerini ek olarak ayıklamak için isteğe bağlı bağlamayı kullanmalısınız (yani, isteğe bağlı öğeler dolaylı olarak Boole değerlerine dönüştürülmez), Swift 2'nin çalışırken kıyamet piramidinden kaçınmak için guardifadeyi eklediğini belirtmek gerekir. birden fazla isteğe bağlı değerle.

Başka bir deyişle, seçenekleriniz artık açıkça aşağıdakileri kontrol etmeyi içerir nil:

if xyz != nil {
    // Do something with xyz
}

İsteğe bağlı ciltleme:

if let xyz = xyz {
    // Do something with xyz
    // (Note that we can reuse the same variable name)
}

Ve guardifadeler:

guard let xyz = xyz else {
    // Handle failure and then exit this code block
    // e.g. by calling return, break, continue, or throw
    return
}

// Do something with xyz, which is now guaranteed to be non-nil

Birden fazla isteğe bağlı değer olduğunda, sıradan isteğe bağlı bağlamanın nasıl daha fazla girintiye yol açabileceğini unutmayın:

if let abc = abc {
    if let xyz = xyz {
        // Do something with abc and xyz
    }        
}

Bu iç içe yerleştirmeyi guardifadelerle önleyebilirsiniz :

guard let abc = abc else {
    // Handle failure and then exit this code block
    return
}

guard let xyz = xyz else {
    // Handle failure and then exit this code block
    return
}

// Do something with abc and xyz

1
yukarıda belirtildiği gibi, iç içe geçmiş isteğe bağlı istatistiklerden kurtulmak için de bunu yapabilirsiniz. if let abc = abc? .xyz? .something {}
Suhaib

1
@Suhaib Ah, true: İç içe özelliklere hızlı bir şekilde erişmek için isteğe bağlı zincirlemeyi de ( developer.apple.com/library/prerelease/content/documentation/… ) kullanabilirsiniz. Burada bahsetmedim çünkü orijinal sorudan çok fazla teğet olabileceğini düşündüm.
Chris Frederick

Mükemmel cevap! Ama Swift, OMG ... programcı dostu olmaktan hala çok uzak!
turingtested

@turingtested Teşekkürler! Aslında, bunun yanlışlıkla bir değer kullanmaya çalışmayacağınızı garanti etmesi açısından programcı dostu olduğunu düşünüyorum nil, ancak öğrenme eğrisinin biraz dik olduğunu kabul ediyorum. :)
Chris Frederick

4

Swift 5 Protokol Uzantısı

Protokol uzantısını kullanan bir yaklaşım, böylece isteğe bağlı sıfır kontrolünü kolayca satır içine alabilirsiniz:

import Foundation

public extension Optional {

    var isNil: Bool {

        guard case Optional.none = self else {
            return false
        }

        return true

    }

    var isSome: Bool {

        return !self.isNil

    }

}

kullanım

var myValue: String?

if myValue.isNil {
    // do something
}

if myValue.isSome {
    // do something
}

Bu cevap hakkında birkaç not aldım ve nedenini bilmek istiyorum. En azından aşağıya inecekseniz yorum yapın.
Brody Robertson

1
Muhtemelen dili bir çeşit pitonik saflıkta tutmaya çalışanın swiftanalogudur pythonistas. Benim almam? Kodunuzu Utils miksime kaldırdım.
javadba

2

Şimdi, objektif-c'nin birazını geri kazanmanıza izin veren aşağıdaki şeyi hızlıca yapabilirsiniz. if nil else

if textfieldDate.text?.isEmpty ?? true {

}

2

Bunun yerine if, üçlü işleç bir şeyin sıfır olup olmadığına bağlı olarak bir değer elde etmek istediğinizde kullanışlı olabilir:

func f(x: String?) -> String {
    return x == nil ? "empty" : "non-empty"
}

xyz == nilifade işe yaramaz ama x != nilişe yarar. Neden bilmiyorum.
Andrew

2

İsteğe bağlı bağlamayı yapmak için ifveya guardifadeleri kullanmanın yanı sıra başka bir yaklaşım , aşağıdakilerle genişletmektir Optional:

extension Optional {

    func ifValue(_ valueHandler: (Wrapped) -> Void) {
        switch self {
        case .some(let wrapped): valueHandler(wrapped)
        default: break
        }
    }

}

ifValuebir kapanış alır ve isteğe bağlı nil olmadığında değeri bağımsız değişken olarak çağırır. Bu şekilde kullanılır:

var helloString: String? = "Hello, World!"

helloString.ifValue {
    print($0) // prints "Hello, World!"
}

helloString = nil

helloString.ifValue {
    print($0) // This code never runs
}

Swift programcıları tarafından kullanılan en geleneksel (dolayısıyla tanıdık) yaklaşımlar olduğu için muhtemelen bir ifveya guardyine de kullanmalısınız .


2

Özel olarak ele alınmayan bir seçenek Swift'in yok sayılan değer sözdizimini kullanmaktır:

if let _ = xyz {
    // something that should only happen if xyz is not nil
}

nilSwift gibi modern bir dilde kontrol edilemediğinden bunu sevdim . Sanırım yersiz hissetmesinin nedeni niltemelde bir sentinel değer olmasıdır. Modern programlamada hemen hemen her yerde nöbetçilerle işimizi bitirdik, bu yüzden nilgitmesi gerektiği gibi geliyor.


1

Ayrıca kullanabilirsiniz Nil-Coalescing Operator

nil-coalescing operator( a ?? b) İsteğe bağlı bir unwraps aiçerdiği takdirde adeğer veya döner avarsayılan değeri bisea bir nil. A ifadesi her zaman isteğe bağlı türdedir. İfade b, içinde saklanan türle eşleşmelidir a.

let value = optionalValue ?? defaultValue

Eğer optionalValue , nilotomatik olarakdefaultValue


Bunu beğendim, çünkü varsayılan bir değer belirtmek kolay bir seçenek sunuyor.
thowa

0
var xyz : NSDictionary?

// case 1:
xyz = ["1":"one"]
// case 2: (empty dictionary)
xyz = NSDictionary() 
// case 3: do nothing

if xyz { NSLog("xyz is not nil.") }
else   { NSLog("xyz is nil.")     }

Bu test her durumda beklendiği gibi çalıştı. BTW, parantez gerekmez ().


2
OP endişe edilir bir durumdur: if (xyz != nil).
zaph

0

Şartlıysanız ve paketini açmak ve karşılaştırmak istiyorsanız, bileşik boole ifadesinin kısa devre değerlendirmesinden yararlanmaya ne dersiniz?

if xyz != nil && xyz! == "some non-nil value" {

}

Kabul edilirse, bu, önerilen diğer gönderilerden bazıları kadar okunabilir değil, ancak işi önerilen ve diğer önerilen çözümlerden biraz özlü hale getiriyor.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.