Trường hợp 1 và trường hợp 2 giống nhau - cả hai đều là bài tập nội dung của a
cho một biến mới. Sự khác biệt duy nhất là bạn đang rời Swift để suy ra loại unwrapped_a
trong tùy chọn 1, trong khi bạn đang tự đưa ra loại trong tùy chọn 2. Lý do chính bạn cần làm tùy chọn 2 là nếu giá trị nguồn không rõ ràng - ví dụ nếu nó là một hàm quá tải có thể trả về nhiều loại.
Trường hợp 3 khá thú vị.
Bất cứ khi nào bạn có giá trị, Swift sẽ luôn sẵn sàng nâng cấp âm thầm lên giá trị gói tùy chọn nếu nó giúp loại khớp và mã biên dịch. Tự động nâng cấp nhanh các loại khá hiếm (ví dụ: nó sẽ không nâng cấp hoàn toàn một số Int16
lên ví dụ Int32
) nhưng các giá trị cho tùy chọn là một ngoại lệ.
Điều này có nghĩa bạn có thể vượt qua giá trị bất cứ nơi nào một tùy chọn là cần thiết mà không cần phải bận tâm để quấn nó:
func f(maybe: Int?) { ... }
let i = 1
// you can just pass a straight value:
f(i)
// Swift will turn this into this:
f(Optional(i))
Vì vậy, trong ví dụ cuối cùng của bạn, bạn đã nói với Swift bạn muốn not_unwrapped_a
là một Int?
.Nhưng nó là một phần của một số let
yêu cầu a
phải được mở trước khi được gán cho nó.
Trình bày với điều này, cách duy nhất Swift có thể làm cho nó hoạt động là để bọc hoàn toàn a
trong tùy chọn khác, do đó, đó là những gì nó làm. Bây giờ nó là một tùy chọn có chứa một tùy chọn có chứa nil. Đó không phải là một tùy chọn không có giá trị - đó là một tùy chọn có chứa một giá trị (của một tùy chọn có chứa nil). Unwrapping cung cấp cho bạn một tùy chọn có chứa nil. Nó có vẻ như như không có gì xảy ra. Nhưng nó đã làm - nó đã được bọc một lần thứ hai, sau đó unwrapped một lần.
Bạn có thể thấy điều này hoạt động nếu bạn biên dịch mã mẫu của mình bằng swiftc -dump-ast source.swift
. Bạn sẽ thấy cụm từ inject_into_optional implicit type='Int??’
. Int??
là tùy chọn có chứa tùy chọn.
Tùy chọn chứa các tùy chọn không làm mờ các trường hợp cạnh - chúng có thể xảy ra dễ dàng. Ví dụ: nếu bạn đã từng ... trong một mảng chứa các tùy chọn hoặc sử dụng một chỉ số để lấy giá trị từ một từ điển chứa các tùy chọn, thì các tùy chọn của các tùy chọn đã được tham gia vào quá trình đó.
Một cách khác để suy nghĩ về việc này là nếu bạn nghĩ về if let x = y { }
như loại * như một chức năng, if_let
, được xác định như sau:
func if_let<T>(optVal: T?, block: T->()) {
if optVal != nil {
block(optVal!)
}
}
Bây giờ hãy tưởng tượng nếu bạn cung cấp một block
rằng mất một Int?
- đó là, T
sẽ là Int?
. Vì vậy, T?
sẽ là Int??
. Khi bạn đã chuyển một số Int?
thông thường thành if_let
cùng với khối đó, Swift sau đó sẽ ngầm nâng cấp nó lên một số Int??
để biên dịch. Đó chính là những gì đang xảy ra với if let not_unwrapped_a:Int?
. Tôi đồng ý, nâng cấp tùy chọn tiềm ẩn đôi khi có thể gây ngạc nhiên (thậm chí đáng ngạc nhiên hơn là Swift sẽ nâng cấp các chức năng trả về tùy chọn, tức là nếu một chức năng mất (Int)->Int?
, nó sẽ nâng cấp (Int)->Int
để trả lại tùy chọn thay thế). Nhưng có lẽ cảm giác là sự nhầm lẫn tiềm năng đáng giá để thuận tiện trong trường hợp này.
* chỉ loại
Cho Antonio dưới đây, trường hợp cuối cùng khiến tôi trở nên dư thừa và không cần thiết. Mục đích của 'if let' là viết tắt của các tùy chọn unwrapping và chỉ thực thi khi không phải là nil. Trường hợp thứ 3 là nói "nếu giá trị nil/non-nil này là nil hoặc không nil (tất nhiên là nó), hãy thực thi khối bất kể", đó là điều xảy ra. Nó có hiệu quả giống như sử dụng câu lệnh 'if' thông thường, bỏ qua tính năng an toàn' nếu let' cung cấp khi thất bại về giá trị nil. Bạn sẽ phải xử lý các giá trị 'nil' một lần nữa trong khối, mà đánh bại mục đích của cả hai tùy chọn và ràng buộc tùy chọn. – mc01