In biến tùy chọn


118

Tôi đang thử với những dòng mã này

class Student {
    var name: String
    var age: Int?

    init(name: String) {
        self.name = name
    }

    func description() -> String {
        return age != nil ? "\(name) is \(age) years old." : "\(name) hides his age."
    }
}

var me = Student(name: "Daniel")
println(me.description())
me.age = 18
println(me.description())

Đoạn mã trên tạo ra như sau

Daniel hides his age.
Daniel is Optional(18) years old.

Câu hỏi của tôi là tại sao lại có Tùy chọn (18) ở đó, làm cách nào để xóa tùy chọn và chỉ in

Daniel is 18 years old.

Câu trả lời:


190

Bạn phải hiểu Tùy chọn thực sự là gì. Nhiều người mới bắt đầu sử dụng Swift nghĩ var age: Int?rằng tuổi là một Int có thể có giá trị hoặc không. Nhưng nó có nghĩa là độ tuổi là một Tùy chọn có thể có hoặc có thể không giữ Int.

Bên trong description()hàm của bạn, bạn không in Int mà thay vào đó bạn in Tùy chọn. Nếu bạn muốn in Int, bạn phải mở gói Tùy chọn. Bạn có thể sử dụng "ràng buộc tùy chọn" để mở một Tùy chọn:

if let a = age {
 // a is an Int
}

Nếu bạn chắc chắn rằng Tùy chọn giữ một đối tượng, bạn có thể sử dụng "buộc mở gói":

let a = age!

Hoặc trong ví dụ của bạn, vì bạn đã có kiểm tra nil trong hàm mô tả, bạn chỉ có thể thay đổi nó thành:

func description() -> String {
    return age != nil ? "\(name) is \(age!) years old." : "\(name) hides his age."
}

Tôi nghĩ ví dụ sẽ tốt hơn một chút nếu bạn thay đổi nó để sử dụngif let age = age { return ""} else { return "" }
kubi

13
+1 cho:Many Swift beginners think var age: Int? means that age is an Int which may or may not have a value. But it means that age is an Optional which may or may not hold an Int.
lee

18

Một tùy chọn có nghĩa là Swift không hoàn toàn chắc chắn liệu giá trị có tương ứng với kiểu: ví dụ, Int? có nghĩa là Swift không hoàn toàn chắc chắn liệu số đó có phải là Int hay không.

Để loại bỏ nó, có ba phương pháp bạn có thể sử dụng.

1) Nếu bạn hoàn toàn chắc chắn về loại, bạn có thể sử dụng dấu chấm than để buộc mở nó ra, như sau:

// Here is an optional variable:

var age: Int?

// Here is how you would force unwrap it:

var unwrappedAge = age!

Nếu bạn buộc mở một tùy chọn và nó bằng nil, bạn có thể gặp phải lỗi sập sau:

nhập mô tả hình ảnh ở đây

Điều này không nhất thiết phải an toàn, vì vậy đây là một phương pháp có thể ngăn chặn sự cố trong trường hợp bạn không chắc chắn về loại và giá trị:

Phương pháp 2 và 3 bảo vệ chống lại vấn đề này.

2) Tùy chọn không được gói gọn

 if let unwrappedAge = age {

 // continue in here

 }

Lưu ý rằng kiểu không được bao bọc bây giờ là Int , thay vì Int? .

3) Tuyên bố bảo vệ

 guard let unwrappedAge = age else { 
   // continue in here
 }

Từ đây, bạn có thể tiếp tục và sử dụng biến chưa được bao bọc. Đảm bảo chỉ buộc mở (với!), Nếu bạn chắc chắn về loại biến.

Chúc may mắn với dự án của bạn!


1
sử dụng return age! = nil? "(tên) là (tuổi!) tuổi." : "(tên) giấu tuổi của anh ấy."
leedream

1
Đã cứu tôi khỏi đau đầu với một vấn đề tôi gặp phải. Không thể cảm ơn đủ.
Bruno Recillas

8

Đối với mục đích kiểm tra / gỡ lỗi, tôi thường muốn xuất các tùy chọn dưới dạng chuỗi mà không phải luôn kiểm tra các nilgiá trị, vì vậy tôi đã tạo một toán tử tùy chỉnh.

Tôi đã cải thiện mọi thứ hơn nữa sau khi đọc câu trả lời này trong một câu hỏi khác .

fileprivate protocol _Optional {
    func unwrappedString() -> String
}

extension Optional: _Optional {
    fileprivate func unwrappedString() -> String {
        switch self {
        case .some(let wrapped as _Optional): return wrapped.unwrappedString()
        case .some(let wrapped): return String(describing: wrapped)
        case .none: return String(describing: self)
        }
    }
}

postfix operator ~? { }
public postfix func ~? <X> (x: X?) -> String {
    return x.unwrappedString
}

Rõ ràng là toán tử (và các thuộc tính của nó) có thể được điều chỉnh theo ý muốn của bạn hoặc bạn có thể biến nó thành một hàm. Dù sao, điều này cho phép bạn viết mã đơn giản như sau:

var d: Double? = 12.34
print(d)     // Optional(12.34)
print(d~?)   // 12.34
d = nil
print(d~?)   // nil

Việc tích hợp ý tưởng giao thức của người khác đã làm nên điều này thậm chí hoạt động với các tùy chọn lồng nhau, thường xảy ra khi sử dụng chuỗi tùy chọn. Ví dụ:

let i: Int??? = 5
print(i)              // Optional(Optional(Optional(5)))
print("i: \(i~?)")    // i: 5

1
Kể từ Swift 3, bạn cũng có thể sử dụng hàm debugPrint của thư viện chuẩn Swift (_: split: terminator :). Tuy nhiên, điều này sẽ in chuỗi trong dấu ngoặc kép.
psmythirl

@psmythirl: Thật tốt khi biết! Tuy nhiên, đôi khi bạn có thể chỉ muốn chuyển hoặc nhúng một tùy chọn làm tùy chọn Stringở một nơi khác (ví dụ: nhật ký / thông báo lỗi).
Jeremy

print (d as Any)
DawnSong 28/02/18

7

Cập nhật

Đơn giản chỉ cần sử dụng me.age ?? "Unknown age!". Nó hoạt động trong 3.0.2.

Câu trả lời cũ

Nếu không có lực lượng mở (không có tín hiệu mach / sự cố nếu không) một cách hay ho khác để làm điều này sẽ là:

(result["ip"] ?? "unavailable").description.

result["ip"] ?? "unavailable" cũng nên có công việc, nhưng nó không, ít nhất không phải trong 2.2

Tất nhiên, thay thế "không có sẵn" bằng bất cứ điều gì phù hợp với bạn: "nil", "không tìm thấy", v.v.


4

Để mở gói sử dụng tùy chọn age!thay vì age. Hiện tại bạn đang in giá trị tùy chọn có thể là nil. Đó là lý do tại sao nó được quấn với Optional.


4

Trong một số trường hợp, nhanh Optionallà thứ có thể nilxảy ra. Nếu bạn chắc chắn 100% rằng a variablesẽ luôn có một số giá trị và sẽ không trả về nilphần bổ sung !với biến để buộc mở nó ra.

Trong trường hợp khác, nếu bạn không chắc chắn nhiều về giá trị thì hãy thêm một if letkhối hoặc guardđể đảm bảo rằng giá trị đó tồn tại, nếu không nó có thể dẫn đến sự cố.

Đối với if letkhối:

if let abc = any_variable {
 // do anything you want with 'abc' variable no need to force unwrap now.
}

Đối với guardtuyên bố:

guard là một cấu trúc có điều kiện để trả về điều khiển nếu điều kiện không được đáp ứng.

Tôi thích sử dụng guardover if letblock trong nhiều trường hợp vì nó cho phép chúng tôi trả về functiongiá trị nếu một giá trị cụ thể không tồn tại. Giống như khi có một hàm mà một biến là tích phân tồn tại, chúng ta có thể kiểm tra nó trong câu lệnh bảo vệ và trả về nó không tồn tại. I E;

guard let abc = any_variable else { return }

Nếu biến tồn tại, chúng ta có thể sử dụng 'abc' trong hàm bên ngoài phạm vi bảo vệ.


3

agelà kiểu tùy chọn: Optional<Int>vì vậy nếu bạn so sánh nó với nil, nó trả về false mọi lúc nếu nó có giá trị hoặc nếu nó không có. Bạn cần mở gói tùy chọn để nhận giá trị.

Trong ví dụ của bạn, bạn không biết nó có chứa bất kỳ giá trị nào không, vì vậy bạn có thể sử dụng nó để thay thế:

if let myAge = age {
    // there is a value and it's currently undraped and is stored in a constant
}
else {
   // no value
}

3

Tôi đã làm điều này để in giá trị của chuỗi (thuộc tính) từ một bộ điều khiển chế độ xem khác.

ViewController.swift

var testString:NSString = "I am iOS Developer"

SecondViewController.swift

var obj:ViewController? = ViewController(nibName: "ViewController", bundle: nil)
print("The Value of String is \(obj!.testString)")

Kết quả :

The Value of String is I am iOS Developer

2
Bạn không nên ép buộc Unwrap một tùy chọn
E-Riddie

3

Kiểm tra guardtuyên bố:

for student in class {
    guard let age = student.age else { 
        continue 
     }
    // do something with age
}

3

Khi có giá trị mặc định:

print("\(name) is \(age ?? 0) years old")

hoặc khi tên là tùy chọn:

print("\(name ?? "unknown") is \(age) years old")


1

Tôi nhận được Tùy chọn ("Chuỗi") trong các ô trong chế độ xem bảng của mình.

Câu trả lời đầu tiên là tuyệt vời. Và đã giúp tôi tìm ra nó. Đây là những gì tôi đã làm, để giúp đỡ những tân binh ngoài kia như tôi.

Vì tôi đang tạo một mảng trong đối tượng tùy chỉnh của mình, tôi biết rằng nó sẽ luôn có các mục ở vị trí đầu tiên, vì vậy tôi có thể buộc mở nó vào một biến khác. Sau đó, sử dụng biến đó để in hoặc trong trường hợp của tôi, đặt thành văn bản ô trong chế độ xem bảng.

let description = workout.listOfStrings.first!
cell.textLabel?.text = description

Bây giờ có vẻ rất đơn giản, nhưng tôi đã mất một thời gian để tìm ra.


-1

Đây không phải là câu trả lời chính xác cho câu hỏi này, nhưng là một lý do cho loại vấn đề này. Trong trường hợp của tôi, tôi không thể xóa Tùy chọn khỏi Chuỗi với "nếu cho phép" và "bảo vệ cho phép".

Vì vậy, hãy sử dụng AnyObject thay vì Any để xóa tùy chọn khỏi một chuỗi nhanh chóng.

Vui lòng tham khảo liên kết để có câu trả lời.

https://stackoverflow.com/a/51356716/8334818

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.