Swift: Kiểm tra tùy chọn cho con số không


137

Tôi đang sử dụng Xcode 6 Beta 4. Tôi gặp tình huống kỳ lạ này khi tôi không thể tìm ra cách kiểm tra thích hợp cho các tùy chọn.

Nếu tôi có một xyz tùy chọn, là cách chính xác để kiểm tra:

if (xyz) // Do something

hoặc là

if (xyz != nil) // Do something

Các tài liệu nói phải làm theo cách thứ nhất, nhưng tôi thấy rằng đôi khi, cách thứ hai là bắt buộc và không tạo ra lỗi trình biên dịch, nhưng lần khác, cách thứ hai tạo ra lỗi trình biên dịch.

Ví dụ cụ thể của tôi là sử dụng trình phân tích cú pháp XML GData được bắc cầu để nhanh chóng:

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

if (xmlError != nil)

Ở đây, nếu tôi vừa làm:

if xmlError

nó sẽ luôn luôn trở về đúng Tuy nhiên, nếu tôi làm:

if (xmlError != nil)

sau đó nó hoạt động (như cách nó hoạt động trong Objective-C).

Có cái gì với XML GData và cách nó xử lý các tùy chọn mà tôi đang thiếu không?


4
Chúng tôi có thể xem một ví dụ đầy đủ cho trường hợp bất ngờ và trường hợp lỗi của bạn không? (Và tôi biết điều đó thật khó khăn, nhưng hãy cố gắng bắt đầu đánh mất các dấu ngoặc quanh các điều kiện!)
Matt Gibson

1
điều này đã được thay đổi trong Xcode 6 beta 5
newacct


Tôi vừa cập nhật câu hỏi với trường hợp bất ngờ.
tng

Tôi chưa cập nhật lên beta 5. Tôi sẽ làm điều đó sớm thôi; thật tốt khi thấy ?? trong Swift, và hành vi tùy chọn phù hợp hơn.
tng

Câu trả lời:


172

Trong Xcode Beta 5, họ không còn cho phép bạn làm điều đó:

var xyz : NSString?

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

Điều này tạo ra một lỗi:

không tuân thủ giao thức 'BooleanType.Protocol'

Bạn phải sử dụng một trong các hình thức sau:

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

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

5
Ai đó có thể giải thích lý do tại sao xyz == nil không hoạt động không?
Christoph

Ví dụ thứ hai nên đọc: // Làm gì đó bằng xy (đây là điểm khác biệt chính trong các trường hợp sử dụng giữa hai biến thể)
Alper

@Christoph: Thông báo lỗi là hằng số 'xyz' được sử dụng trước khi bị vô hiệu hóa. Tôi nghĩ bạn cần phải thêm '= nil' vào cuối dòng khai báo của xyz.
dùng3207158

Đối với những người mới bắt đầu nhanh như tôi và đang tự hỏi: Bạn vẫn phải mở khóa xyz bên trong khối if. Mặc dù vậy, nó vẫn an toàn, ngay cả khi bạn ép buộc
Omar

@Omar Đó là vẻ đẹp của hình thức thứ hai, bạn sử dụng xy bên trong khối mà không cần phải mở khóa.
Erik Doernenburg

24

Để thêm vào các câu trả lời khác, thay vì gán cho một biến có tên khác bên trong một ifđiều kiện:

var a: Int? = 5

if let b = a {
   // do something
}

bạn có thể sử dụng lại tên biến như thế này:

var a: Int? = 5

if let a = a {
    // do something
}

Điều này có thể giúp bạn tránh hết tên biến sáng tạo ...

Điều này tận dụng lợi thế của bóng đổ biến được hỗ trợ trong Swift.


18

Một trong những cách trực tiếp nhất để sử dụng tùy chọn là:

Giả sử xyzlà loại tùy chọn, Int?ví dụ như.

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

Bằng cách này, bạn có thể kiểm tra cả nếu xyzchứa một giá trị và nếu vậy, ngay lập tức làm việc với giá trị đó.

Liên quan đến lỗi trình biên dịch của bạn, loại UInt8không phải là tùy chọn (lưu ý không '?') Và do đó không thể được chuyển đổi thành nil. Hãy chắc chắn rằng biến bạn đang làm việc là một tùy chọn trước khi bạn coi nó như một biến.


1
Tôi thấy phương pháp này là xấu vì tôi tạo một biến mới (không đổi) một cách không cần thiết, ans phải tạo một tên mới mà hầu hết thời gian sẽ tồi tệ hơn trước. Làm cho tôi có nhiều thời gian hơn để viết và cho người đọc.
Oliveras

3
Đó là phương pháp tiêu chuẩn và phương pháp được đề xuất để mở khóa một biến tùy chọn. "'Nếu cho" là mạnh mẽ nghiêm trọng.
gnasher729

@ gnasher729 nhưng nếu bạn chỉ muốn sử dụng phần khác
Brad Thomas

15

Swift 3.0, 4.0

Chủ yếu có hai cách kiểm tra tùy chọn cho con số không. Dưới đây là những ví dụ so sánh giữa chúng

1. nếu để

if letlà cách cơ bản nhất để kiểm tra tùy chọn cho con số không. Các điều kiện khác có thể được thêm vào kiểm tra con số không này, được phân tách bằng dấu phẩy. Biến không được bằng không để di chuyển cho điều kiện tiếp theo. Nếu chỉ cần kiểm tra bằng không, hãy xóa các điều kiện bổ sung trong đoạn mã sau.

Ngoài ra, nếu xkhông phải là con số không, thì việc đóng if sẽ được thực thi và x_valsẽ có sẵn bên trong. Nếu không, đóng cửa khác được kích hoạt.

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

}

2. bảo vệ hãy

guard letcó thể làm những điều tương tự. Mục đích chính của nó là làm cho nó hợp lý hơn. Nó giống như nói Hãy chắc chắn rằng biến không phải là con số không, nếu không thì dừng chức năng . guard letcũng có thể làm thêm kiểm tra điều kiện như if let.

Sự khác biệt là giá trị không được bao bọc sẽ có sẵn trên cùng một phạm vi guard let, như thể hiện trong bình luận bên dưới. Điều này cũng dẫn đến thời điểm đó trong việc đóng cửa khác, chương trình có để thoát khỏi phạm vi hiện tại, bởi return, breakvv

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

11

Từ hướng dẫn lập trình nhanh chóng

Nếu Tuyên bố và Buộc Unwrapping

Bạn có thể sử dụng câu lệnh if để tìm hiểu xem một tùy chọn có chứa giá trị hay không. Nếu một tùy chọn không có giá trị, nó đánh giá là đúng; nếu nó không có giá trị gì cả, nó đánh giá là sai.

Vì vậy, cách tốt nhất để làm điều này là

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

và nếu bạn đang sử dụng xyzcâu lệnh in if. Bạn có thể mở khóa xyzcâu lệnh if trong biến không đổi. Vì vậy, bạn không cần phải mở khóa mọi vị trí trong câu lệnh if xyzđược sử dụng.

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

Quy ước này được đề xuất bởi applevà nó sẽ được theo sau bởi các nhà phát triển.


2
Trong Swift 3 bạn phải làm if xyz != nil {...}.
psmythirl

Trong Swift 3 tùy chọn không thể được sử dụng như boolean. Thứ nhất bởi vì nó là một C-ism không bao giờ nên có trong ngôn ngữ ở vị trí đầu tiên và thứ hai vì nó gây ra sự nhầm lẫn tuyệt đối với Bool tùy chọn.
gnasher729

9

Mặc dù bạn vẫn phải so sánh rõ ràng một tùy chọn với nilhoặc sử dụng ràng buộc tùy chọn để trích xuất thêm giá trị của nó (tức là các tùy chọn không được chuyển đổi hoàn toàn thành các giá trị Boolean), đáng chú ý là Swift 2 đã thêm guardcâu lệnh để tránh kim tự tháp diệt vong khi làm việc với nhiều giá trị tùy chọn.

Nói cách khác, các tùy chọn của bạn hiện bao gồm kiểm tra rõ ràng cho nil:

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

Ràng buộc tùy chọn:

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

guardtuyên bố:

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

Lưu ý cách liên kết tùy chọn thông thường có thể dẫn đến thụt lề lớn hơn khi có nhiều hơn một giá trị tùy chọn:

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

Bạn có thể tránh việc lồng nhau này bằng các guardcâu lệnh:

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
như đã đề cập ở trên, bạn cũng có thể làm điều này để loại bỏ các statmenets tùy chọn lồng nhau. if let abc = abc? .xyz?
.s Something

1
@Suhaib Ah, đúng: Bạn cũng có thể sử dụng chuỗi tùy chọn ( developer.apple.com/l Library / protelease / content / document / / ) để truy cập nhanh các thuộc tính lồng nhau. Tôi đã không đề cập đến nó ở đây bởi vì tôi nghĩ rằng nó có thể là quá nhiều tiếp tuyến từ câu hỏi ban đầu.
Chris Frederick

Câu trả lời chính xác! Nhưng Swift, OMG ... vẫn còn lâu mới trở nên thân thiện với lập trình viên!
turingtested

@turingtested Cảm ơn! Trên thực tế, tôi nghĩ rằng đây chương trình thân thiện với ý nghĩa rằng nó đảm bảo bạn sẽ không vô tình cố gắng sử dụng một nilgiá trị, nhưng tôi đồng ý rằng đường cong học tập hơi dốc. :)
Chris Frederick

4

Mở rộng giao thức Swift 5

Đây là một cách tiếp cận sử dụng phần mở rộng giao thức để bạn có thể dễ dàng nội tuyến kiểm tra con số tùy chọn:

import Foundation

public extension Optional {

    var isNil: Bool {

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

        return true

    }

    var isSome: Bool {

        return !self.isNil

    }

}

Sử dụng

var myValue: String?

if myValue.isNil {
    // do something
}

if myValue.isSome {
    // do something
}

Tôi đã nhận được một vài lời khen về câu trả lời này và muốn biết tại sao. Ít nhất là nhận xét nếu bạn sẽ downvote.
Brody Robertson

1
Có lẽ đó là sự swifttương tự của việc pythonistascố gắng giữ ngôn ngữ ở một dạng tinh khiết pythonic nào đó. Lấy của tôi? Tôi vừa nâng mã của bạn vào mixin Utils của tôi.
javadba

2

Bây giờ bạn có thể thực hiện nhanh chóng những điều sau đây cho phép bạn lấy lại một chút mục tiêu-c if nil else

if textfieldDate.text?.isEmpty ?? true {

}

2

Thay vì if, toán tử ternary có thể có ích khi bạn muốn nhận giá trị dựa trên việc có thứ gì đó không:

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

xyz == nilbiểu hiện không hoạt động, nhưng x != nilhoạt động. Không biết tại sao.
Andrew

2

Một cách tiếp cận khác ngoài việc sử dụng ifhoặc các guardcâu lệnh để thực hiện ràng buộc tùy chọn là mở rộng Optionalvới:

extension Optional {

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

}

ifValuenhận được một bao đóng và gọi nó với giá trị là một đối số khi tùy chọn không phải là không. Nó được sử dụng theo cách này:

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

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

helloString = nil

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

Bạn có thể nên sử dụng một ifhoặc guardtuy nhiên vì đó là những cách tiếp cận thông thường nhất (do đó quen thuộc) được sử dụng bởi các lập trình viên Swift.


2

Một tùy chọn chưa được đề cập cụ thể là sử dụng cú pháp giá trị bị bỏ qua của Swift:

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

Tôi thích điều này vì việc kiểm tra nilcảm giác lạc lõng trong một ngôn ngữ hiện đại như Swift. Tôi nghĩ rằng lý do nó cảm thấy lạc lõng nillà về cơ bản là một giá trị trọng tâm. Chúng tôi đã loại bỏ các Sentinel khá nhiều ở mọi nơi khác trong lập trình hiện đại nên nilcảm giác như nó cũng nên đi.


1

Ngoài ra bạn có thể sử dụng Nil-Coalescing Operator

Các nil-coalescing operator( a ?? b) unwraps một tùy chọn anếu nó chứa agiá trị, hoặc trở về agiá trị mặc định bnếu anil. Biểu thức a luôn là một loại tùy chọn. Biểu thức bphải phù hợp với loại được lưu trữ bên trong a.

let value = optionalValue ?? defaultValue

Nếu optionalValuenil, nó tự động gán giá trị chodefaultValue


Tôi thích điều đó, bởi vì nó cung cấp tùy chọn dễ dàng để chỉ định một giá trị mặc định.
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.")     }

Thử nghiệm này đã làm việc như mong đợi trong tất cả các trường hợp. BTW, bạn không cần dấu ngoặc ().


2
Trường hợp mà OP quan tâm là : if (xyz != nil).
zaph

0

Nếu bạn có điều kiện và muốn mở khóa và so sánh, làm thế nào về việc tận dụng đánh giá ngắn mạch của biểu thức boolean tổng hợp như trong

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

}

Cấp, điều này không thể đọc được như một số bài viết được đề xuất khác, nhưng hoàn thành công việc và hơi ngắn gọn hơn các giải pháp được đề xuất khác.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.