2014-12-19 18 views
6

Hãy nói rằng chúng ta có:nếu chúng ta hãy hành động kỳ lạ khi xác định rõ ràng loại

let a:Int? = nil 

// block not executed - unwapping done, type is inferred 
if let unwrapped_a = a { 
    println(unwrapped_a) 
} 

// block not executed - unwrapping done, type is specified 
if let unwrapped_a:Int = a { 
    println(unwrapped_a) 
} 

// block gets executed - unwrapping not done, new local constant + assignment is done instead? 
if let not_unwrapped_a:Int? = a { 
    println(not_unwrapped_a) 
} 

Vì vậy, tôi nên cho rằng Swift không một unwrapping trong trường hợp đầu tiên, nhưng chuyển nhượng trong trường hợp thứ hai?

Cú pháp này không quá gần để tạo ra sự nhầm lẫn? Tôi có nghĩa là, có, trình biên dịch cảnh báo bạn rằng bạn đang sử dụng một loại tùy chọn khi làm việc với not_unwrapped_a, nhưng vẫn còn.

Cập nhật:

Vì vậy, sau khi trả lời tốc độ bay Velocity của tôi thấy khác (nhưng thực chất giống nhau) trường hợp kỳ lạ:

if let not_unwrapped_a:Int???? = a { 
    println(not_unwrapped_a) 
} 

a sẽ được âm thầm bọc trong một Int????. Vì vậy, nó sẽ là một loại Int????? (năm) - bởi vì a đã là một tùy chọn. Và sau đó nó sẽ nhận được unwrapped một lần.

+2

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

Trả lời

5

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

4

Mục đích của ràng buộc tùy chọn là kiểm tra tùy chọn không phải là nil, unwrap và gán cho một tùy chọn không thuộc loại được đính kèm trong tùy chọn. Vì vậy, theo ý kiến ​​của tôi, trường hợp đầu tiên là cách sử dụng đúng - biến thể duy nhất tôi sẽ sử dụng là thêm một diễn viên tùy chọn (hữu ích cho ví dụ khi tùy chọn chứa AnyObject).

tôi sẽ không sử dụng trường hợp thứ 2, thích một dàn diễn viên không bắt buộc:

if let unwrapped_a = a as? Int { ... } 

trừ, như ghi nhận của @drewag trong ý kiến, loại được quy định một cách rõ ràng để tránh sự mơ hồ khi nó không phải là rõ ràng.

Theo tôi, trường hợp thứ 3 sẽ tạo ra lỗi biên dịch. Tôi không thấy bất kỳ việc sử dụng ràng buộc tùy chọn nào để gán một tùy chọn cho một tùy chọn khác. Ràng buộc tùy chọn không phải là gán nội tuyến chung (chẳng hạn như if let x = 5 {...}), do đó khái niệm không nên hoạt động nếu bên trái là tùy chọn - nó phải được xử lý giống như bên phải không phải là tùy chọn (thay vào đó biên dịch không thành công).

+3

Nó là hoàn toàn hợp lệ và hợp lý để sử dụng trường hợp thứ hai nếu bạn quyết định điều quan trọng là loại có mặt để giúp dễ đọc. Đôi khi nó không phải là rõ ràng những gì loại một biến là khi nhìn vào bối cảnh địa phương và một số chú thích thêm, ngay cả khi không cần thiết, có thể được tốt cho dễ đọc. Làm nó như là một diễn viên tùy chọn sẽ ngụ ý một không phải là kiểu 'Int'. Điều này có thể làm cho mã khó hiểu hơn. – drewag

+0

@drewag: Tôi đồng ý nó sẽ làm cho mã ít mơ hồ hơn, và có, một diễn viên thường được hiểu là "nó có thể thuộc loại khác". Tuy nhiên, và đây chỉ là một sở thích cá nhân *, tôi sẽ không sử dụng nó bởi vì tôi nghĩ rằng mã xung quanh nên giải quyết sự mơ hồ. Tuy nhiên tôi đang cập nhật câu trả lời, điều bạn nói quan trọng là – Antonio

+0

Thật khó hiểu trong trường hợp này và thậm chí có thể không hữu ích nhưng tôi không nghĩ đó là lỗi trình biên dịch, Swift chỉ áp dụng nhất quán logic nâng cấp ngầm của nó cho các giá trị cho các tùy chọn.Lỗi vỏ đặc biệt có thể dẫn đến nhiều sự nhầm lẫn hơn thông qua sự không nhất quán. –

Các vấn đề liên quan