Dấu chấm than có nghĩa gì trong ngôn ngữ Swift?


518

Hướng dẫn Ngôn ngữ lập trình Swift có ví dụ sau:

class Person {
    let name: String
    init(name: String) { self.name = name }
    var apartment: Apartment?
    deinit { println("\(name) is being deinitialized") }
}

class Apartment {
    let number: Int
    init(number: Int) { self.number = number }
    var tenant: Person?
    deinit { println("Apartment #\(number) is being deinitialized") }
}

var john: Person?
var number73: Apartment?

john = Person(name: "John Appleseed")
number73 = Apartment(number: 73)

//From Apple's “The Swift Programming Language” guide (https://developer.apple.com/library/content/documentation/Swift/Conceptual/Swift_Programming_Language/AutomaticReferenceCounting.html)

Sau đó, khi giao căn hộ cho người đó, họ sử dụng một dấu chấm than để "mở khóa ví dụ":

john!.apartment = number73

"Unrap the dụ" nghĩa là gì? Tại sao cần thiết? Nó khác với việc chỉ làm như sau:

john.apartment = number73

Tôi rất mới với ngôn ngữ Swift. Chỉ cần cố gắng để có được những điều cơ bản xuống.


CẬP NHẬT:
Phần lớn của câu đố mà tôi đã bỏ lỡ (không được nêu trực tiếp trong các câu trả lời - ít nhất là tại thời điểm viết bài này) là khi bạn làm như sau:

var john: Person?

điều đó KHÔNG có nghĩa là " johnthuộc loại Personvà nó có thể là con số không", như tôi nghĩ ban đầu. Tôi chỉ đơn giản là hiểu lầm rằng PersonPerson?là loại hoàn toàn riêng biệt. Khi tôi nắm bắt được điều đó, tất cả những điều khác ?, !sự điên rồ, và những câu trả lời tuyệt vời dưới đây, có ý nghĩa hơn rất nhiều.

Câu trả lời:


530

"Unrap the dụ" nghĩa là gì? Tại sao cần thiết?

Theo như tôi có thể làm việc (điều này cũng rất mới đối với tôi) ...

Thuật ngữ "bọc" ngụ ý chúng ta nên nghĩ về một biến Tùy chọn như một món quà, được bọc trong giấy sáng bóng, có thể (đáng buồn thay!) Để trống .

Khi "được bọc", giá trị của biến Tùy chọn là một enum có hai giá trị có thể (hơi giống Boolean). Enum này mô tả xem biến có giữ một giá trị ( Some(T)) hay không ( None).

Nếu có một giá trị, điều này có thể đạt được bằng cách "hủy ghép" biến (lấy Ttừ Some(T)).

Làm thế nào john!.apartment = number73khác với john.apartment = number73? (Giải thích)

Nếu bạn viết tên của một biến Tùy chọn (ví dụ: văn bản john, không có !), thì điều này đề cập đến enum "được bao bọc" (Một số / Không), chứ không phải giá trị của chính nó (T). Vì vậy, johnđây không phải là một ví dụ Personvà nó không có apartmentthành viên:

john.apartment
// 'Person?' does not have a member named 'apartment'

PersonGiá trị thực tế có thể được mở ra theo nhiều cách khác nhau:

  • "cưỡng bức hủy kết nối": john!(đưa ra Persongiá trị nếu nó tồn tại, lỗi thời gian chạy nếu không)
  • "ràng buộc tùy chọn": if let p = john { println(p) }(thực thi printlnnếu giá trị tồn tại)
  • "Chuỗi tùy chọn": john?.learnAboutSwift()(thực hiện phương thức tạo này nếu giá trị tồn tại)

Tôi đoán bạn chọn một trong những cách này để mở khóa, tùy thuộc vào những gì sẽ xảy ra trong trường hợp không, và khả năng đó là như thế nào. Thiết kế ngôn ngữ này buộc trường hợp không được xử lý rõ ràng, mà tôi cho rằng cải thiện sự an toàn so với Obj-C (nơi dễ quên để xử lý trường hợp không).

Cập nhật :

Dấu chấm than cũng được sử dụng trong cú pháp khai báo "Các tùy chọn không bị che giấu hoàn toàn".

Trong các ví dụ cho đến nay, johnbiến đã được khai báo là var john:Person?và nó là Tùy chọn. Nếu bạn muốn giá trị thực của biến đó, bạn phải mở khóa nó, sử dụng một trong ba phương pháp trên.

Nếu nó được khai báo var john:Person!thay vào đó, biến sẽ là một tùy chọn không bị che giấu hoàn toàn (xem phần có tiêu đề này trong cuốn sách của Apple). Không cần mở khóa loại biến này khi truy cập giá trị và johncó thể được sử dụng mà không cần cú pháp bổ sung. Nhưng cuốn sách của Apple nói:

Các tùy chọn không được bao hàm hoàn toàn không nên được sử dụng khi có khả năng một biến trở thành con số không tại một điểm sau đó. Luôn sử dụng loại tùy chọn thông thường nếu bạn cần kiểm tra giá trị không trong suốt vòng đời của biến.

Cập nhật 2 :

Bài viết " Các tính năng Swift thú vị " của Mike Ash cung cấp một số động lực cho các loại tùy chọn. Tôi nghĩ rằng nó là tuyệt vời, văn bản rõ ràng.

Cập nhật 3 :

Một bài viết hữu ích khác về việc sử dụng tùy chọn không được tiết lộ cho dấu chấm than: " Swift and the Last Mile " của Chris Adamson. Bài báo giải thích rằng đây là một biện pháp thực dụng của Apple được sử dụng để khai báo các loại được sử dụng bởi các khung Objective-C có thể chứa nil. Tuyên bố một loại là tùy chọn (sử dụng ?) hoặc ngầm định (sử dụng !) là "sự đánh đổi giữa an toàn và thuận tiện". Trong các ví dụ được đưa ra trong bài viết, Apple đã chọn khai báo các loại là ngầm định, giúp mã cuộc gọi thuận tiện hơn, nhưng kém an toàn hơn.

Có lẽ Apple có thể kết hợp thông qua các khuôn khổ của họ trong tương lai, loại bỏ sự không chắc chắn của các tham số ngầm ("có lẽ không bao giờ") và thay thế chúng bằng các tùy chọn ("chắc chắn có thể là không hoàn toàn cụ thể [hy vọng, được ghi lại!]" Khai báo -optional ("không bao giờ là không"), dựa trên hành vi chính xác của mã Objective-C của chúng.


Tôi không chắc về lời giải thích này. Nếu bạn chỉ chạy mã mà không có! nó vẫn trả về giá trị thực. Có thể là ! là cho tốc độ?
Richard Washington

ĐỒNG Ý. Vì vậy, các tài liệu nói về việc sử dụng! khi bạn biết chắc chắn nó có thể được mở ra. Nhưng bạn có thể chạy mã tốt mà không cần nó (một tùy chọn thứ tư cho danh sách của bạn - bỏ ẩn ngầm) VÀ không cần kiểm tra trước. Bạn nhận lại giá trị hoặc nil nếu không. Nhưng nếu bạn biết chắc chắn rằng nó không phải là con số không thì hãy sử dụng! ...... nhưng tôi vẫn không thể hiểu tại sao bạn lại làm điều này?
Richard Washington

2
Xin chào @RichardWashington - Tôi đã thêm một bản cập nhật cho câu trả lời của mình, hy vọng sẽ làm rõ một số điều này.
Ashley

Cập nhật 3 chính xác là những gì tôi đang tìm kiếm. Trước khi đọc nó, tôi đã nghĩ rằng Apple đã nói dối khi họ trả lại một cái gì đó giống như NSURLCredential! mà thực sự có thể là con số không.
Ngón tay cái vàng

Cảm ơn câu trả lời tuyệt vời @Ashley. Sau khi đọc câu trả lời này và một số mã nhanh chóng của Apple, tôi thực sự thấy rằng trong sự đánh đổi giữa an toàn và năng suất, thông thường tốt nhất là ở bên an toàn hơn. Một ngoại lệ đáng chú ý mà tôi đã tìm thấy là khi Apple sử dụng tính năng hủy kết nối bắt buộc đối với các thành phần UI, điều này có ý nghĩa bởi vì trước tiên, các yếu tố UI thường là tùy chọn khi liên quan đến bảng phân cảnh (vì chúng không được gán giá trị theo chương trình) và thứ hai vì chúng gần như không bao giờ nil trừ khi bộ điều khiển xem chứa của họ là nil, trong trường hợp này thảo luận nếu moot.
Mihir

127

Đây là những gì tôi nghĩ là sự khác biệt:

var john: Person?

Có nghĩa là John có thể không

john?.apartment = number73

Trình biên dịch sẽ diễn giải dòng này là:

if john != nil {
    john.apartment = number73
}

Trong khi

john!.apartment = number73

Trình biên dịch sẽ diễn giải dòng này đơn giản là:

john.apartment = number73

Do đó, sử dụng! sẽ hủy bỏ câu lệnh if và làm cho nó chạy nhanh hơn, nhưng nếu john không, thì sẽ xảy ra lỗi thời gian chạy.

Vì vậy, bọc ở đây không có nghĩa là nó được bọc bộ nhớ, nhưng nó có nghĩa là nó được bọc mã, trong trường hợp này, nó được gói bằng một câu lệnh if và vì Apple rất chú ý đến hiệu năng trong thời gian chạy, họ muốn cung cấp cho bạn một cách để làm cho ứng dụng của bạn chạy với hiệu suất tốt nhất có thể.

Cập nhật:

Trở lại câu trả lời này sau 4 năm, vì tôi đã nhận được danh tiếng cao nhất từ ​​nó trong Stackoverflow :) Tôi đã hiểu nhầm một chút về ý nghĩa của việc mở khóa tại thời điểm đó. Bây giờ sau 4 năm tôi tin rằng ý nghĩa của việc mở khóa ở đây là mở rộng mã từ dạng rút gọn ban đầu của nó. Ngoài ra, nó có nghĩa là loại bỏ sự mơ hồ xung quanh đối tượng đó, vì chúng tôi không chắc chắn theo định nghĩa của nó là không hay không. Giống như câu trả lời của Ashley ở trên, hãy nghĩ về nó như một món quà không chứa gì trong đó. Nhưng tôi vẫn nghĩ rằng unrapping là mã unrapping và không dựa trên bộ nhớ như sử dụng enum.


9
Trong sân chơi của tôi john.apemony = number73 không biên dịch, bạn phải chỉ định john? .Apidor = number73
Chuck Pinkert

2
@ChuckPinkert đã đúng, dòng thứ 4 nên được chỉnh sửa thành john? .Apidor = number73, mặc dù câu trả lời hay!
Bruce

john.apemony = number73 đưa ra lỗi: giá trị của loại tùy chọn 'Person?' không được mở khóa: bạn có nghĩa là sử dụng '!' hoặc là '?'?
nhện

4
Unrapping không có gì để thực hiện wrt. "Kiểm tra không" vẫn phải được thực hiện trong thời gian chạy, điểm khác biệt duy nhất là lỗi thời gian chạy sẽ bị ném trong trường hợp không có giá trị nào xuất hiện trong Tùy chọn.
madidier

67

TL; DR

Dấu chấm than có nghĩa gì trong ngôn ngữ Swift?

Dấu chấm than có hiệu quả nói rằng, tôi biết rằng tùy chọn này chắc chắn có giá trị; vui lòng sử dụng nó Điều này được gọi là buộc hủy bỏ giá trị của tùy chọn:

Thí dụ

let possibleString: String? = "An optional string."
print(possibleString!) // requires an exclamation mark to access its value
// prints "An optional string."

let assumedString: String! = "An implicitly unwrapped optional string."
print(assumedString)  // no exclamation mark is needed to access its value
// prints "An implicitly unwrapped optional string."

Nguồn: https://developer.apple.com/lvern/content/documentation/Swift/Conceptual/Swift_Programming_L Language / TheBasics.html


11
Câu trả lời của bạn là tuyệt vời bởi vì tôi hiểu những gì đang xảy ra bây giờ. Điều tôi không hiểu là tại sao lại tồn tại các tùy chọn không được bao bọc. Tại sao tạo một cái gì đó được định nghĩa là một Chuỗi tùy chọn không được bao bọc hoàn toàn, thay vì một loại Chuỗi thông thường? Việc sử dụng của họ sau đó là như nhau. Tôi đang thiếu gì?
pachun

Điều này dường như không còn đúng nữa. Trong một thay thế Swift 5, nếu tôi làm let f: String! = "hello"và sau đó print(f), đầu ra Optional("hello")thay vì chỉ "hello".
numbermaniac

37

Nếu john là một var tùy chọn (được khai báo như vậy)

var john: Person?

sau đó có thể john không có giá trị (theo cách nói của ObjC, giá trị nil)

Dấu chấm than về cơ bản nói với trình biên dịch "Tôi biết cái này có giá trị, bạn không cần phải kiểm tra nó". Nếu bạn không muốn sử dụng nó, bạn có thể kiểm tra một cách có điều kiện:

if let otherPerson = john {
    otherPerson.apartment = number73
}

Nội thất của điều này sẽ chỉ đánh giá nếu john có một giá trị.


4
Cảm ơn đã phản ứng, bây giờ tôi hiểu những gì các dấu chấm than nói, tôi vẫn đang cố gắng để quấn quanh đầu tôi tại sao ... Bạn nói nó nói với trình biên dịch "Tôi biết điều này có giá trị, bạn không cần phải kiểm tra cho nó ". Nó mua gì cho tôi khi trình biên dịch không kiểm tra nó? Nếu không có dấu chấm than, trình biên dịch sẽ đưa ra lỗi (tôi chưa có môi trường, nơi tôi có thể kiểm tra Swift)? Là ! 100% cần thiết cho tất cả các lọ tùy chọn? Nếu vậy, tại sao Apple lại bận tâm với nó, trái ngược với việc chỉ làm cho nó trở nên thiếu thốn ! có nghĩa là "Tôi biết điều này có giá trị, bạn không cần phải kiểm tra nó"?
Troy

"là trình biên dịch sẽ đưa ra một lỗi" - không, nó vẫn hoạt động tốt, trả về giá trị như mong đợi. Tôi không hiểu điều này. Là ! chỉ cho tốc độ khi bạn chắc chắn có thể?
Richard Washington

Trong thực tế sau này, các tài liệu nói về các tùy chọn ngầm định được sử dụng bằng ví dụ dưới đây: “let possibleString: String? = "An optional string." println(possibleString!) // requires an exclamation mark to access its value // prints "An optional string.” Nhưng nó hoạt động tốt với không! Một cái gì đó có vẻ kỳ lạ ở đây.
Richard Washington

3
@RichardWashington Bạn đang bối rối vì lý do chính đáng! Các ví dụ trong các tài liệu đang bỏ qua một số thứ và gây hiểu lầm bởi vì tất cả những gì họ làm là sử dụng println. Rõ ràng, có ít nhất một vài trường hợp không yêu cầu hủy ghép tùy chọn. Một là khi sử dụng println. Khác là khi sử dụng nội suy chuỗi. Vì vậy, có thể nội suy println và chuỗi đang mở ra dưới bìa? Có lẽ ai đó có cái nhìn sâu sắc hơn về điều này. Nhìn vào các định nghĩa tiêu đề Xcode6 Beta không tiết lộ cho tôi bất cứ điều gì về phép thuật này.
vui nhộn

Vâng, tôi hiểu điều đó John?Johnlà hai loại khác nhau. Một người thuộc loại Người tùy chọn và Người loại khác. Người tùy chọn cần được mở khóa trước khi bạn có thể đưa Người ra. Nhưng như bạn nói, điều này dường như xảy ra ít nhất trong những trường hợp này mà không phải thực sự làm gì cả. Điều này dường như để làm cho! dư thừa. Trừ khi ! là LUÔN tùy chọn nhưng là một điều được đề xuất để bắt lỗi biên dịch thời gian. Kiểu như gán vars / cho phép một loại cụ thể có thể rõ ràng let John: Person = ...nhưng cũng có thể được suy ra let John = ....
Richard Washington

27

Một số phối cảnh hình ảnh lớn để thêm vào các câu trả lời hữu ích nhưng chi tiết hơn khác:

Trong Swift, dấu chấm than xuất hiện trong một số ngữ cảnh:

  • Buộc mở khóa: let name = nameLabel!.text
  • Các tùy chọn không được bao hàm hoàn toàn: var logo: UIImageView!
  • Đúc cưỡng bức: logo.image = thing as! UIImage
  • Các trường hợp ngoại lệ chưa được xử lý: try! NSJSONSerialization.JSONObjectWithData(data, [])

Mỗi một trong số này là một cấu trúc ngôn ngữ khác nhau với một ý nghĩa khác nhau, nhưng chúng đều có ba điểm quan trọng chung:

1. Dấu chấm than phá vỡ kiểm tra an toàn thời gian biên dịch của Swift.

Khi bạn sử dụng !trong Swift, bạn là chủ yếu nói, “Này, biên dịch, tôi biết bạn nghĩ rằng một lỗi có thể xảy ra ở đây, nhưng tôi biết với tổng chắc chắn rằng nó sẽ không bao giờ.”

Không phải tất cả các mã hợp lệ đều phù hợp với hộp của hệ thống loại thời gian biên dịch của Swift - hoặc kiểm tra loại tĩnh của bất kỳ ngôn ngữ nào , cho vấn đề đó. Có những tình huống bạn có thể chứng minh một cách hợp lý rằng một lỗi sẽ không bao giờ xảy ra, nhưng bạn không thể chứng minh nó với trình biên dịch . Đó là lý do tại sao các nhà thiết kế của Swift đã thêm các tính năng này ngay từ đầu.

Tuy nhiên, bất cứ khi nào bạn sử dụng !, bạn đều loại trừ lỗi đường dẫn khôi phục, điều đó có nghĩa là

2. Dấu chấm than là sự cố tiềm ẩn.

Một câu cảm thán cũng nói rằng, Hey Hey Swift, tôi rất chắc chắn rằng lỗi này không bao giờ có thể xảy ra rằng bạn nên đánh sập toàn bộ ứng dụng của mình hơn là để tôi viết mã đường dẫn khôi phục cho nó.

Đó là một khẳng định nguy hiểm. Nó có thể là một câu đúng: trong mã quan trọng trong nhiệm vụ mà bạn đã suy nghĩ kỹ về sự bất biến của mã, có thể là đầu ra không có thật còn tệ hơn một sự cố.

Tuy nhiên, khi tôi nhìn thấy !ngoài tự nhiên, nó hiếm khi được sử dụng một cách tỉnh táo như vậy. Thay vào đó, điều đó thường có nghĩa là, giá trị này là tùy chọn và tôi thực sự không suy nghĩ quá nhiều về lý do tại sao nó có thể không hoặc làm thế nào để xử lý tình huống đó một cách hợp lý, nhưng việc thêm !nó đã biên dịch mã sao cho mã của tôi là chính xác, phải không?

Coi chừng sự kiêu ngạo của dấu chấm than. Thay thế…

3. Dấu chấm than được sử dụng tốt nhất một cách tiết kiệm.

Mỗi một trong các !cấu trúc này có một ?bản sao buộc bạn phải xử lý trường hợp lỗi / không:

  • Unrapping có điều kiện: if let name = nameLabel?.text { ... }
  • Tùy chọn: var logo: UIImageView?
  • Diễn viên có điều kiện: logo.image = thing as? UIImage
  • Trường hợp ngoại lệ không thành công: try? NSJSONSerialization.JSONObjectWithData(data, [])

Nếu bạn muốn sử dụng !, luôn luôn tốt để xem xét cẩn thận tại sao bạn không sử dụng ?thay thế. Là sự cố chương trình của bạn thực sự là lựa chọn tốt nhất nếu !hoạt động không thành công? Tại sao giá trị đó là tùy chọn / có sẵn?

Có đường dẫn khôi phục hợp lý mà mã của bạn có thể thực hiện trong trường hợp không / lỗi không? Nếu vậy, mã nó.

Nếu nó không thể là con số không, nếu lỗi không bao giờ có thể xảy ra, thì có cách nào hợp lý để làm lại logic của bạn để trình biên dịch biết điều đó không? Nếu vậy, hãy làm điều đó; mã của bạn sẽ ít bị lỗi hơn.

Đôi khi không có cách hợp lý để xử lý lỗi và chỉ cần bỏ qua lỗi - và do đó tiến hành xử lý dữ liệu sai - sẽ tồi tệ hơn so với sự cố. Đó là những lần sử dụng vũ lực tháo gỡ.

Tôi định kỳ tìm kiếm toàn bộ cơ sở mã của mình !và kiểm tra mỗi lần sử dụng nó. Rất ít tập quán đứng lên để xem xét. (Theo văn bản này, toàn bộ khung Siesta có chính xác hai trường hợp của nó.)

Điều đó không có nghĩa là bạn không bao giờ nên sử dụng !mã của mình - chỉ là bạn nên sử dụng nó một cách thận trọng và không bao giờ biến nó thành tùy chọn mặc định.


func isSubscriptionActive(receiptData: NSDictionary?) -> Bool { if(receiptData == nil) { return false; } return (hasValidTrial(receiptData!) || isNotExpired(receiptData!)) && isNotCancelled(receiptData!) } Cho 3. có cách nào tốt hơn để viết nó không?
jenson-button-event

Bạn có thể nóifunc isSubscriptionActive(receiptData: NSDictionary?) -> Bool { guard let nonNilReceiptData = receiptData else { return false} return (hasValidTrial(nonNilReceiptData) || isNotExpired(nonNilReceiptData)) && isNotCancelled(nonNilReceiptData) }
rkgibson2

Dấu chấm than không phá vỡ bất kỳ kiểm tra an toàn. Bạn nhận được một sự cố được đảm bảo nếu tùy chọn là không. Các tùy chọn luôn luôn được kiểm tra. Đó chỉ là một cách rất tàn bạo để kiểm tra an toàn.
gnasher729

@ gnasher729 Như câu trả lời, nó phá vỡ các kiểm tra an toàn thời gian biên dịch có lợi cho lỗi thời gian chạy an toàn .
Paul Cantrell

24

johnlà một tùy chọn var. Vì vậy, có thể được chứa một nilgiá trị. Để đảm bảo rằng giá trị không phải là không sử dụng !ở cuối vartên.

Từ tài liệu

Một khi bạn chắc chắn rằng tùy chọn không chứa giá trị, bạn có thể truy cập giá trị cơ bản của nó bằng cách thêm dấu chấm than (!) Vào cuối tên tùy chọn. Dấu chấm than có hiệu quả nói rằng, tôi biết rằng tùy chọn này chắc chắn có giá trị; vui lòng sử dụng nó

Một cách khác để kiểm tra giá trị không phải là không

    if let j = json {
        // do something with j
    }

1
Vâng, tôi đã thấy rằng trong tài liệu, nhưng dường như vẫn không cần thiết ... vì, đối với tôi, john.apartment = number73cũng nói "Tôi biết rằng tùy chọn này chắc chắn có giá trị; vui lòng sử dụng nó." ...
Troy

6
Đúng vậy, nhưng vấn đề là mặc định Swift cố gắng bắt lỗi tại thời điểm biên dịch. Nếu bạn biết hoặc nghĩ rằng bạn biết rằng biến này không thể chứa số 0, bạn có thể xóa kiểm tra đó bằng cách sử dụng dấu chấm than.
lassej

16

Dưới đây là một số ví dụ:

var name:String = "Hello World"
var word:String?

Trường hợp wordlà một giá trị tùy chọn. có nghĩa là nó có thể hoặc không chứa một giá trị.

word = name 

Ở đây namecó một giá trị để chúng ta có thể gán nó

var cow:String = nil
var dog:String!

Trường hợp dogđược mở ra mạnh mẽ có nghĩa là nó phải chứa một giá trị

dog = cow

Ứng dụng sẽ sập bởi vì chúng tôi được chỉ định nilđể mở khóa


1
var c:Int = nilsẽ nhận được: "Không thể khởi tạo loại được chỉ định 'int'"
Asususer

15

Trong trường hợp này...

var John: Người!

điều đó có nghĩa là, ban đầu John sẽ có giá trị bằng 0, nó sẽ được đặt và một khi được đặt sẽ không bao giờ được dẫn lại nữa. Do đó, để thuận tiện, tôi có thể sử dụng cú pháp dễ dàng hơn để truy cập vào một var tùy chọn vì đây là "tùy chọn hoàn toàn chưa được mở"


1
Cảm ơn bạn. Tôi cũng đã tìm thấy liên kết sau đây giải thích điều này. Loại này được gọi là "tùy chọn ngầm định". stackoverflow.com/questions/24122601/
Mạnh

5

Nếu bạn đến từ ngôn ngữ họ C, bạn sẽ nghĩ "con trỏ tới đối tượng loại X có thể là địa chỉ bộ nhớ 0 (NULL)" và nếu bạn đến từ một ngôn ngữ được gõ động, bạn sẽ suy nghĩ "Đối tượng có lẽ thuộc loại X nhưng có thể là loại không xác định". Cả hai điều này đều không chính xác, mặc dù trong một đường vòng, đường đầu tiên gần.

Cách bạn nên nghĩ về nó như thể nó là một đối tượng như:

struct Optional<T> {
   var isNil:Boolean
   var realObject:T
}

Khi bạn đang kiểm tra giá trị tùy chọn của mình với giá trị foo == nilthực sự trở lại foo.isNilvà khi bạn nói foo!nó sẽ trả về foo.realObjectvới một xác nhận rằng foo.isNil == false. Điều quan trọng cần lưu ý điều này bởi vì nếu foothực sự là không khi bạn thực hiện foo!, đó là lỗi thời gian chạy, vì vậy thông thường bạn muốn sử dụng một điều kiện cho phép thay vì trừ khi bạn rất chắc chắn rằng giá trị sẽ không bằng không. Loại mánh khóe này có nghĩa là ngôn ngữ có thể được gõ mạnh mà không buộc bạn phải kiểm tra nếu các giá trị không ở khắp mọi nơi.

Trong thực tế, nó không thực sự hoạt động như vậy bởi vì công việc được thực hiện bởi trình biên dịch. Ở mức cao, có một loại Foo?tách biệt Foovà ngăn các func chấp nhận loại Fookhông nhận giá trị nil, nhưng ở mức thấp, giá trị tùy chọn không phải là đối tượng thực sự vì nó không có thuộc tính hoặc phương thức; Trên thực tế, đó có thể là một con trỏ có thể bằng NULL (0) với thử nghiệm thích hợp khi hủy bỏ lực lượng.

Có một tình huống khác trong đó bạn thấy một dấu chấm than là trên một loại, như trong:

func foo(bar: String!) {
    print(bar)
}

Điều này gần tương đương với việc chấp nhận một tùy chọn với một unrap cưỡng bức, tức là:

func foo(bar: String?) {
    print(bar!)
}

Bạn có thể sử dụng phương pháp này để có một phương pháp chấp nhận một giá trị kỹ thuật tùy chọn nhưng sẽ có lỗi thời gian chạy nếu không. Trong phiên bản hiện tại của Swift, điều này dường như bỏ qua khẳng định không phải là không nên thay vào đó bạn sẽ gặp lỗi cấp thấp. Nói chung không phải là một ý tưởng tốt, nhưng nó có thể hữu ích khi chuyển đổi mã từ ngôn ngữ khác.


3

Các ! có nghĩa là bạn đang buộc mở khóa đối tượng! sau Thông tin thêm có thể được tìm thấy trong tài liệu Táo, có thể tìm thấy ở đây: https://developer.apple.com/l Library / ios / document / swift / conception / Swift_Programming_Lingu / TheBasics.html


3

Nếu bạn quen thuộc với C #, thì đây giống như các loại Nullable cũng được khai báo bằng dấu chấm hỏi:

Person? thisPerson;

Và dấu chấm than trong trường hợp này tương đương với việc truy cập thuộc tính .Value thuộc loại nullable như thế này:

thisPerson.Value

2

Trong các biến C mục tiêu không có giá trị bằng 'nil' (cũng có thể sử dụng các giá trị 'nil' giống như 0 và false), do đó có thể sử dụng các biến trong các câu lệnh có điều kiện (Biến có giá trị giống như 'TRUE 'và những người không có giá trị bằng' FALSE ').

Swift cung cấp loại an toàn bằng cách cung cấp 'giá trị tùy chọn'. tức là nó ngăn ngừa các lỗi hình thành từ việc gán các biến của các loại khác nhau.

Vì vậy, trong Swift, chỉ booleans có thể được cung cấp trên các câu điều kiện.

var hw = "Hello World"

Ở đây, mặc dù 'hw' là một chuỗi, nó không thể được sử dụng trong câu lệnh if như trong mục tiêu C.

//This is an error

if hw

 {..}

Cho rằng nó cần phải được tạo ra như,

var nhw : String? = "Hello World"

//This is correct

if nhw

 {..}

2

Các ! ở phần cuối của một đối tượng nói rằng đối tượng là một tùy chọn và hủy bỏ nếu nó có thể trả về một con số không. Điều này thường được sử dụng để bẫy các lỗi có thể làm sập chương trình.


2

Tóm lại (!): Sau khi bạn đã khai báo một biến và bạn chắc chắn biến đó đang giữ một giá trị.

let assumedString: String! = "Some message..."
let implicitString: String = assumedString

nếu không, bạn sẽ phải làm điều này sau mỗi lần chuyển giá trị ...

let possibleString: String? = "An optional string."
let forcedString: String = possibleString! // requires an exclamation mark

1

John là một Người tùy chọn, có nghĩa là nó có thể giữ một giá trị hoặc là con số không.

john.apartment = number73

được sử dụng nếu john không phải là một tùy chọn. Vì john không bao giờ là con số không, chúng tôi có thể chắc chắn rằng nó sẽ không gọi căn hộ theo giá trị không. Trong khi

john!.apartment = number73

hứa với trình biên dịch rằng john không phải là con số không, sau đó mở ra tùy chọn để nhận giá trị của john và truy cập vào tài sản căn hộ của john. Sử dụng điều này nếu bạn biết rằng john không phải là con số không. Nếu bạn gọi đây là tùy chọn không, bạn sẽ gặp lỗi thời gian chạy.

Tài liệu này bao gồm một ví dụ hay để sử dụng điều này trong đó convertNumber là một tùy chọn.

if convertedNumber {
    println("\(possibleNumber) has an integer value of \(convertedNumber!)")
} else {
    println("\(possibleNumber) could not be converted to an integer")
}

Bạn có nói rằng nếu john không, và tôi đi john.apartment = number73, thì đó sẽ không gây ra lỗi trừ khi tôi đặt '!' sau john?
Troy

Nếu john là một tùy chọn, bạn phải sử dụng dấu chấm than để truy cập các thuộc tính của nó bởi vì trình biên dịch phải biết rằng nó không phải là con số không. Nếu nó không phải là một tùy chọn, bạn không thể sử dụng dấu chấm than.
Connor

1
Nếu tôi phải sử dụng một dấu chấm than cho TẤT CẢ các lọ tùy chọn, thì tại sao Apple thậm chí còn bận tâm với nó, trái ngược với việc làm cho nó thiếu dấu chấm than có nghĩa tương tự? Có vẻ như không cần thiết phình to ... Hoặc có trường hợp KHÔNG nên sử dụng dấu chấm than với một var tùy chọn?
Troy

bạn sử dụng dấu chấm than khi bạn cần tùy chọn không được không. Giống như trong trường hợp với các thuộc tính truy cập của john. bạn có thể làm một cái gì đó như var jack: Person? = john trong đó jack cũng là một jack tùy chọn hoặc var: Person = john! trong đó jack là một người và không phải là con số không
Connor

Đúng nhưng, trong ví dụ ban đầu, thực tế là tôi không tham gia hội thảo johntùy chọn nói với trình biên dịch rằng tôi cần nó không phải là con số không? ... Và trong var jack: Person = john!ví dụ của bạn , không phải là thiếu? Sau khi Person nói với trình biên dịch rằng bạn cần phải johnkhông? Nhìn chung, !dường như có vẻ hơi dư thừa ... Vẫn có cảm giác như tôi đang thiếu thứ gì đó trong phần "tại sao" của !...
Troy

1

Nói một cách đơn giản, dấu chấm than có nghĩa là một tùy chọn đang được mở ra. Một tùy chọn là một biến có thể có giá trị hay không - vì vậy bạn có thể kiểm tra xem biến đó có trống không, sử dụng câu lệnh if let như hiển thị ở đây , và sau đó buộc mở khóa nó. Nếu bạn buộc gỡ bỏ một tùy chọn trống, chương trình của bạn sẽ bị sập, vì vậy hãy cẩn thận! Tùy chọn được khai báo bằng cách đặt dấu chấm hỏi ở cuối bài tập rõ ràng cho một biến, ví dụ tôi có thể viết:

var optionalExample: String?

Biến này không có giá trị. Nếu tôi mở khóa, chương trình sẽ bị sập và Xcode sẽ cho bạn biết bạn đã cố gắng mở khóa tùy chọn với giá trị bằng 0.

Hy vọng rằng đã giúp.


1

TRONG TỪ ĐƠN GIẢN

SỬ DỤNG Dấu chấm than chỉ ra rằng biến phải bao gồm giá trị không (không bao giờ là không)


1

Toàn bộ câu chuyện bắt đầu với một tính năng của swift được gọi là vars tùy chọn. Đây là những lọ có thể có giá trị hoặc có thể không có giá trị. Nói chung, swift không cho phép chúng tôi sử dụng một biến không được khởi tạo, vì điều này có thể dẫn đến sự cố hoặc lý do không mong muốn và cũng là máy chủ giữ chỗ cho các cửa hậu. Do đó, để khai báo một biến có giá trị ban đầu không được xác định, chúng tôi sử dụng '?'. Khi một biến như vậy được khai báo, để sử dụng nó như là một phần của một biểu thức nào đó, người ta phải mở khóa chúng trước khi sử dụng, hủy ghép là một hoạt động thông qua đó giá trị của biến được phát hiện áp dụng cho các đối tượng. Nếu không hủy kết nối nếu bạn cố gắng sử dụng chúng, bạn sẽ gặp lỗi biên dịch thời gian. Để mở khóa một biến là một tùy chọn var, dấu chấm than "!" Được sử dụng.

Bây giờ, có những lúc bạn biết rằng các biến tùy chọn như vậy sẽ được gán giá trị theo hệ thống chẳng hạn hoặc chương trình của riêng bạn, nhưng sau đó, ví dụ, các cửa hàng UI, trong tình huống như vậy thay vì khai báo một biến tùy chọn bằng dấu chấm hỏi "?" chúng tôi sử dụng "!".

Do đó, hệ thống biết rằng biến này được khai báo bằng "!" là tùy chọn ngay bây giờ và không có giá trị nhưng sẽ nhận được giá trị sau này trong vòng đời của nó.

Do đó, dấu chấm than giữ hai cách sử dụng khác nhau, 1. Để khai báo một biến sẽ là tùy chọn và sẽ nhận được giá trị chắc chắn sau 2. Để hủy một biến tùy chọn trước khi sử dụng nó trong một biểu thức.

Mô tả ở trên tránh quá nhiều thứ kỹ thuật, tôi hy vọng.


1

Nếu bạn sử dụng nó như một tùy chọn, nó sẽ mở ra tùy chọn và xem nếu có cái gì đó ở đó. Nếu bạn sử dụng nó trong câu lệnh if-other là mã KHÔNG. Ví dụ,

if (myNumber != 3){
 // if myNumber is NOT 3 do whatever is inside these brackets.
)

1

Một biến tùy chọn có thể chứa một giá trị hoặc có thể không

trường hợp 1: var myVar:String? = "Something"

trường hợp 2: var myVar:String? = nil

Bây giờ nếu bạn yêu cầu myVar!, bạn đang bảo trình biên dịch trả về một giá trị trong trường hợp 1 thì nó sẽ trả về "Something"

trong trường hợp 2 nó sẽ sụp đổ.

Ý nghĩa ! mark sẽ buộc trình biên dịch trả về một giá trị, ngay cả khi nó không ở đó. đó là lý do tại sao tên Force Unwrapping .


@Moritz. Tôi không biết có phải vậy không? Tôi không thể trả lời nếu một câu hỏi đã được trả lời? Tôi có gây ra vấn đề gì không hoặc có gì sai trong câu trả lời của tôi không? tôi không hiểu tại sao bạn lại hạ thấp nó để trả lời đúng? Tôi đã cố gắng trả lời dựa trên sự hiểu biết của tôi về khái niệm này. tôi nghĩ rằng một số người sẽ thấy nó hữu ích cho lời giải thích rõ ràng và dễ dàng.
Ashish Pisey

@Moritz Bây giờ đây là xây dựng. Bạn nên nói với tôi điều chỉnh này ngay từ đầu, nếu đây là lý do. Cảm ơn vì bạn đã phản hồi. tôi đã sửa nó
Ashish Pisey

0
Simple the Optional variable allows nil to be stored.

var str : String? = nil

str = "Data"

To convert Optional to the Specific DataType, We unwrap the variable using the keyword "!"

func get(message : String){
   return
}

get(message : str!)  // Unwapped to pass as String

1
Vui lòng không viết câu trả lời không thêm bất cứ điều gì chưa được bao phủ bởi các câu trả lời trước đó.
Dalija Prasnikar

-1

TỰ HỎI MÌNH ĐI

  • Liệu loại person?có một apartmentthành viên / tài sản? HOẶC LÀ
  • Liệu loại personcó một apartmentthành viên / tài sản?

Nếu bạn không thể trả lời câu hỏi này, thì hãy tiếp tục đọc:

Để hiểu bạn có thể cần mức độ hiểu biết siêu cơ bản về Generics . Xem ở đây . Rất nhiều thứ trong Swift được viết bằng Generics. Tùy chọn bao gồm

Mã dưới đây đã được cung cấp từ video Stanford này . Rất khuyến khích bạn xem 5 phút đầu tiên

Một tùy chọn là một enum chỉ có 2 trường hợp

enum Optional<T>{
    case None
    case Some(T)
}

let x: String? = nil //actually means:

let x = Optional<String>.None

let x :String? = "hello" //actually means:

let x = Optional<String>.Some("hello")

var y = x! // actually means:

switch x {
case .Some(let value): y = value
case .None: // Raise an exception
}

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

let x:String? = something
if let y = x {
    // do something with y
}
//Actually means:

switch x{
case .Some(let y): print)(y) // or whatever else you like using 
case .None: break
}

Khi bạn nói var john: Person?Bạn thực sự có nghĩa như vậy:

enum Optional<Person>{
case .None
case .Some(Person)
}

Có enum trên có bất kỳ tài sản được đặt tên apartment? Bạn có thấy nó ở bất cứ đâu không? Nó không có ở đó! Tuy nhiên, nếu bạn mở khóa, tức là làm person!thì bạn có thể ... những gì nó làm dưới mui xe là:Optional<Person>.Some(Person(name: "John Appleseed"))


Nếu bạn đã xác định var john: Personthay vì: var john: Person?thì bạn sẽ không còn cần phải !sử dụng nữa, vì Personbản thân nó đã có một thành viên củaapartment


Như một cuộc thảo luận trong tương lai về lý do tại sao việc sử dụng !để mở khóa đôi khi không được khuyến nghị, hãy xem phần Hỏi & Đáp này


2
Sử dụng các slide thực tế ở trên có thể vi phạm bản quyền: "... Đại học Stanford giữ bản quyền cho tất cả nội dung trong bộ sưu tập iTunes U của chúng tôi." , từ http://itunes.stanford.edu . Có lẽ tốt hơn để trả lời, bằng lời nói của bạn, nội dung bạn học được từ khóa học mà bạn cảm nhận được để trả lời câu hỏi này (và thay vì sử dụng các slide, thay vì trích dẫn các phần có liên quan của chúng ở dạng mã, với các tài liệu tham khảo, một cách tự nhiên).
dfri

2
Đối số của bạn là populum quảng cáo argumentum . Dù sao, tôi không tin Stanford sẽ đến đây và thực thi các quyền DMCA , nhưng sẽ luôn tốt hơn nếu câu trả lời của bạn bằng lời nói của bạn dựa trên các nguồn chính thức / hợp lệ và không phải dưới dạng sao chép các nguồn đó hoàn toàn như trên; đặc biệt là khi chúng ta nói về các slide có bản quyền: một lần nữa, tốt hơn là trích dẫn nội dung của các slide trong các khối mã (hình ảnh của mã nói chung là không có tại SO!).
dfri

1
Bài đăng meta mà tôi liên kết chỉ mô tả cách tiếp cận của SO: này, một bài đăng như bài này sẽ tự nhiên không bị xóa nếu một số người dùng ngẫu nhiên báo cáo đó là vi phạm bản quyền: chỉ khi ví dụ như đại diện của Stanford. đã đến đây và vì bài viết sẽ bị xóa nên SO cần phải thi hành nó. Do đó tôi đã viết " Tôi không tin Stanford sẽ đến đây và thực thi các quyền DMCA, ..." . Quan điểm của tôi ở trên là tôi tin rằng điều này có thể vi phạm bản quyền (mà tôi tin là sai), nhưng tự nhiên sẽ không ai thực thi điều này. ...
dfri

1
Nhưng điều này sang một bên, đăng câu hỏi hoặc câu trả lời có chứa hình ảnh của mã là không được khuyến khích vì một số lý do. Để kết thúc: Tôi đã cố gắng chỉ ra những nhận xét này mà tôi tin rằng câu trả lời này, ở dạng hiện tại, không hoàn toàn tốt như có thể, vì hai lý do chính (slide có bản quyền, hình ảnh của mã). Đương nhiên, không ai, kể cả bản thân tôi, sẽ làm bất cứ điều gì về điều này, tôi chỉ đơn giản là chỉ nó để tham khảo trong tương lai (hoặc có thể khuyến khích bạn chỉnh sửa bài đăng này bằng trích dẫn bằng các khối mã thay thế). Đồng ý, iTunes U! :)
dfri

2
@dfri đã xóa hình ảnh
Mật ong
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.