Tại sao lại tạo ra các tùy chọn không rõ ràng, vì điều đó ngụ ý rằng bạn biết có một giá trị?


493

Tại sao bạn lại tạo một "Tùy chọn không bị ràng buộc ngầm" so với việc chỉ tạo một biến thông thường hoặc hằng số? Nếu bạn biết rằng nó có thể được mở khóa thành công thì tại sao lại tạo một tùy chọn ở vị trí đầu tiên? Ví dụ, tại sao lại thế này:

let someString: String! = "this is the string"

sẽ hữu ích hơn:

let someString: String = "this is the string"

Nếu các tùy chọn của Arnold chỉ ra rằng một hằng số hoặc biến số được phép có 'không có giá trị', thì đôi khi, rõ ràng từ cấu trúc của chương trình rằng một tùy chọn sẽ luôn có giá trị sau khi giá trị đó được đặt đầu tiên, đó là điểm của làm cho nó một tùy chọn ở nơi đầu tiên? Nếu bạn biết một tùy chọn sẽ luôn có giá trị, thì điều đó không làm cho nó không phải là tùy chọn?

Câu trả lời:


127

Hãy xem xét trường hợp của một đối tượng có thể có các đặc tính không trong khi nó được xây dựng và định cấu hình, nhưng không thay đổi và không phải là con số không (NSImage thường được xử lý theo cách này, mặc dù trong trường hợp của nó đôi khi vẫn hữu ích để biến đổi). Các tùy chọn không được bao bọc hoàn toàn sẽ làm sạch mã của nó một cách tốt, với sự mất an toàn tương đối thấp (miễn là bảo đảm được giữ, nó sẽ an toàn).

(Chỉnh sửa) Để rõ ràng: tùy chọn thông thường gần như luôn luôn thích hợp hơn.


459

Trước khi tôi có thể mô tả các trường hợp sử dụng cho Tùy chọn chưa được ẩn hoàn toàn, bạn nên hiểu Tùy chọn nào và Tùy chọn chưa được ẩn hoàn toàn trong Swift. Nếu bạn không, tôi khuyên bạn trước tiên nên đọc bài viết của tôi về các tùy chọn

Khi nào nên sử dụng một tùy chọn hoàn toàn không bị ràng buộc

Có hai lý do chính khiến người ta tạo ra một Tùy chọn hoàn toàn không bị ràng buộc. Tất cả phải làm với việc xác định một biến sẽ không bao giờ được truy cập khi nilbởi vì nếu không, trình biên dịch Swift sẽ luôn buộc bạn phải mở khóa tùy chọn một cách rõ ràng.

1. Một hằng số không thể được xác định trong quá trình khởi tạo

Mỗi hằng số thành viên phải có một giá trị khi thời gian khởi tạo hoàn tất. Đôi khi, một hằng số không thể được khởi tạo với giá trị chính xác của nó trong quá trình khởi tạo, nhưng nó vẫn có thể được đảm bảo có giá trị trước khi được truy cập.

Việc sử dụng biến Tùy chọn sẽ khắc phục được sự cố này vì Tùy chọn được tự động khởi tạo nilvà giá trị cuối cùng sẽ chứa sẽ không thay đổi. Tuy nhiên, có thể là một nỗi đau khi liên tục mở ra một biến mà bạn biết chắc chắn là không. Các tùy chọn không bị ràng buộc hoàn toàn đạt được các lợi ích giống như một Tùy chọn với lợi ích bổ sung mà người ta không cần phải mở khóa rõ ràng ở mọi nơi.

Một ví dụ tuyệt vời về điều này là khi một biến thành viên không thể được khởi tạo trong lớp con UIView cho đến khi chế độ xem được tải:

class MyView: UIView {
    @IBOutlet var button: UIButton!
    var buttonOriginalWidth: CGFloat!

    override func awakeFromNib() {
        self.buttonOriginalWidth = self.button.frame.size.width
    }
}

Tại đây, bạn không thể tính chiều rộng ban đầu của nút cho đến khi chế độ xem tải, nhưng bạn biết rằng nó awakeFromNibsẽ được gọi trước bất kỳ phương thức nào khác trên chế độ xem (ngoài khởi tạo). Thay vì buộc giá trị phải được mở ra một cách rõ ràng một cách vô nghĩa trên tất cả các lớp của bạn, bạn có thể khai báo nó là một Tùy chọn hoàn toàn không bị ràng buộc.

2. Khi ứng dụng của bạn không thể phục hồi từ một biến nil

Điều này cực kỳ hiếm, nhưng nếu ứng dụng của bạn không thể tiếp tục chạy nếu có một biến nilkhi được truy cập, sẽ rất lãng phí thời gian để bận tâm kiểm tra nó nil. Thông thường nếu bạn có một điều kiện hoàn toàn đúng để ứng dụng của bạn tiếp tục chạy, bạn sẽ sử dụng một assert. Một tùy chọn hoàn toàn không bị ràng buộc có một khẳng định cho con số không được xây dựng ngay trong nó. Thậm chí sau đó, thường là tốt để mở khóa tùy chọn và sử dụng một xác nhận mô tả nhiều hơn nếu nó là con số không.

Khi không sử dụng một tùy chọn ngầm ẩn

1. Biến thành viên được tính toán nhanh

Đôi khi bạn có một biến thành viên không bao giờ là không, nhưng nó không thể được đặt thành giá trị chính xác trong quá trình khởi tạo. Một giải pháp là sử dụng Tùy chọn chưa được ẩn hoàn toàn, nhưng cách tốt hơn là sử dụng biến lười biếng:

class FileSystemItem {
}

class Directory : FileSystemItem {
    lazy var contents : [FileSystemItem] = {
        var loadedContents = [FileSystemItem]()
        // load contents and append to loadedContents
        return loadedContents
    }()
}

Bây giờ, biến thành viên contentskhông được khởi tạo cho đến khi lần đầu tiên được truy cập. Điều này cho lớp một cơ hội để vào trạng thái chính xác trước khi tính giá trị ban đầu.

Lưu ý: Điều này có vẻ mâu thuẫn với # 1 từ phía trên. Tuy nhiên, có một sự khác biệt quan trọng cần được thực hiện. Ở buttonOriginalWidthtrên phải được đặt trong viewDidLoad để ngăn chặn bất kỳ ai thay đổi độ rộng nút trước khi thuộc tính được truy cập.

2. Ở mọi nơi khác

Đối với hầu hết các phần, nên tránh các tùy chọn không bị che giấu bởi vì nếu sử dụng nhầm, toàn bộ ứng dụng của bạn sẽ bị sập khi được truy cập trong khi nil. Nếu bạn không bao giờ chắc chắn về việc một biến có thể không, luôn luôn mặc định sử dụng Tùy chọn bình thường. Unwrapping một biến không bao giờ nilchắc chắn không làm tổn thương nhiều.


4
Câu trả lời này nên được cập nhật cho phiên bản beta 5. Bạn không còn có thể sử dụng if someOptional.
Ông già Noel

2
@SantaClaus hasValueđược xác định ngay trên Tùy chọn. Tôi thích ngữ nghĩa của hasValuenhững người != nil. Tôi cảm thấy dễ hiểu hơn nhiều đối với những lập trình viên mới chưa sử dụng nilngôn ngữ khác. hasValuehợp lý hơn nhiều so với nil.
vẽ

2
Có vẻ như hasValueđã được lấy từ phiên bản beta 6. Ash đã đưa nó trở lại mặc dù ... github.com/AshFurrow/hasValue
Chris Wagner

1
@newacct Liên quan đến kiểu trả về của trình khởi tạo Objc, nó không chỉ là một tùy chọn ngầm ẩn Unwrapping Tùy chọn. Hành vi bạn mô tả cho việc sử dụng "không tùy chọn" chính xác là những gì mà Tùy chọn không bị ràng buộc hoàn toàn sẽ làm (không thất bại cho đến khi được truy cập). Về việc để chương trình thất bại sớm hơn bằng cách buộc hủy bỏ, tôi đồng ý rằng việc sử dụng một tùy chọn không được ưu tiên, nhưng điều này không phải lúc nào cũng có thể.
vẽ

1
@ thông tin không. Dù thế nào đi chăng nữa, nó sẽ xuất hiện trong Objective-C dưới dạng con trỏ (nếu nó là tùy chọn, hoàn toàn không được bao bọc hoặc không tùy chọn).
drewag

56

Các tùy chọn không được bao bọc hoàn toàn hữu ích để trình bày một thuộc tính là không tùy chọn khi thực sự nó cần phải là tùy chọn dưới vỏ bọc. Điều này thường là cần thiết để "buộc nút" giữa hai đối tượng liên quan mà mỗi đối tượng cần một tham chiếu đến đối tượng kia. Điều này có ý nghĩa khi không tham chiếu nào thực sự là tùy chọn, nhưng một trong số chúng cần phải không trong khi cặp đang được khởi tạo.

Ví dụ:

// These classes are buddies that never go anywhere without each other
class B {
    var name : String
    weak var myBuddyA : A!
    init(name : String) {
        self.name = name
    }
}

class A {
    var name : String
    var myBuddyB : B
    init(name : String) {
        self.name = name
        myBuddyB = B(name:"\(name)'s buddy B")
        myBuddyB.myBuddyA = self
    }
}

var a = A(name:"Big A")
println(a.myBuddyB.name)   // prints "Big A's buddy B"

Bất kỳ Btrường hợp nào cũng phải luôn có một myBuddyAtham chiếu hợp lệ , vì vậy chúng tôi không muốn làm cho người dùng coi nó là tùy chọn, nhưng chúng tôi cần nó là tùy chọn để chúng tôi có thể xây dựng Btrước khi chúng tôi phải Atham khảo.

TUY NHIÊN! Loại yêu cầu tham chiếu lẫn nhau này thường là một dấu hiệu của khớp nối chặt chẽ và thiết kế kém. Nếu bạn thấy mình dựa vào các tùy chọn ngầm định, bạn có thể nên xem xét tái cấu trúc để loại bỏ các phụ thuộc chéo.


7
Tôi nghĩ một trong những lý do họ tạo ra tính năng ngôn ngữ này là@IBOutlet
Jiaaro

11
+1 cho cảnh báo "TUY NHIÊN". Nó có thể không đúng mọi lúc, nhưng chắc chắn đó là điều cần chú ý.
JMD

4
Bạn vẫn có một chu kỳ tham chiếu mạnh mẽ giữa A và B. Các tùy chọn không được bao hàm hoàn toàn KHÔNG tạo ra một tham chiếu yếu. Bạn vẫn cần phải khai báo myByddyA hoặc myBuddyB là yếu (có thể là myBuddyA)
rút ra

6
Để rõ ràng hơn nữa tại sao câu trả lời này là sai và gây hiểu lầm nguy hiểm: Các tùy chọn không bị che giấu hoàn toàn không liên quan gì đến việc quản lý bộ nhớ và không ngăn chặn chu kỳ giữ lại. Tuy nhiên, Tùy chọn không bị che giấu hoàn toàn vẫn hữu ích trong trường hợp được mô tả để thiết lập tham chiếu hai chiều. Vì vậy, chỉ cần thêm weakkhai báo và xóa "mà không tạo chu kỳ giữ mạnh"
drewag

1
@drewag: Bạn nói đúng - Tôi đã chỉnh sửa câu trả lời để xóa chu trình giữ lại. Tôi định làm cho sự phản kháng yếu đi nhưng tôi đoán nó đã trượt tâm trí của tôi.
n8gray

37

Các tùy chọn không được bao hàm hoàn toàn là sự thỏa hiệp thực dụng để làm cho công việc trong môi trường lai phải tương tác với các khung Cacao hiện có và các quy ước của chúng dễ chịu hơn, đồng thời cho phép di chuyển từng bước vào mô hình lập trình an toàn hơn - mà không cần con trỏ null - được thực thi bởi trình biên dịch Swift.

Cuốn sách Swift, trong chương Cơ bản , phần Tùy chọn không bị che giấu nói:

Các tùy chọn không được bao bọc hoàn toàn hữu ích khi giá trị của tùy chọn được xác nhận tồn tại ngay sau khi tùy chọn được xác định lần đầu và chắc chắn có thể được giả sử tồn tại ở mọi thời điểm sau đó. Việc sử dụng chính của các tùy chọn không được bao bọc trong Swift là trong quá trình khởi tạo lớp, như được mô tả trong Tài liệu tham khảo chưa được đặt tên và Các thuộc tính tùy chọn chưa được ẩn .
...
Bạn có thể nghĩ đến một tùy chọn ngầm nào như cho phép các tùy chọn được tháo tự động bất cứ khi nào nó được sử dụng. Thay vì đặt dấu chấm than sau tên tùy chọn mỗi lần bạn sử dụng, bạn đặt dấu chấm than sau loại tùy chọn khi bạn khai báo.

Điều này xuất phát xuống để sử dụng trường hợp không nil-ness tài sản được thành lập thông qua Công ước sử dụng, và không thể được thực thi bởi trình biên dịch trong khởi tạo lớp. Ví dụ: các UIViewControllerthuộc tính được khởi tạo từ NIB hoặc Storyboards, nơi khởi tạo được chia thành các giai đoạn riêng biệt, nhưng sau khi viewDidLoad()bạn có thể giả sử rằng các thuộc tính thường tồn tại. Nếu không, để đáp ứng các trình biên dịch, bạn phải sử dụng các unwrapping buộc , tùy chọn ràng buộc hoặc không bắt buộc chaining chỉ để che khuất mục đích chính của mã này.

Phần trên của cuốn sách Swift cũng đề cập đến chương Đếm tham chiếu tự động :

Tuy nhiên, có một kịch bản thứ ba, trong đó cả hai thuộc tính phải luôn có một giá trị và không thuộc tính nào sẽ không bao giờ được nilkhởi tạo hoàn tất. Trong kịch bản này, sẽ rất hữu ích khi kết hợp một thuộc tính chưa được đặt tên trên một lớp với một thuộc tính tùy chọn chưa được ẩn trong lớp kia.

Điều này cho phép cả hai thuộc tính được truy cập trực tiếp (không cần hủy ghép tùy chọn) khi quá trình khởi tạo hoàn tất, trong khi vẫn tránh được chu trình tham chiếu.

Điều này dẫn đến việc không phải là một ngôn ngữ được thu thập rác, do đó, việc phá vỡ các chu trình giữ lại thuộc về bạn với tư cách là một lập trình viên và các tùy chọn không được bao bọc là một công cụ để che giấu điều này.

Điều đó bao trùm lên Khi nào nên sử dụng các tùy chọn không được bao bọc trong mã của bạn? câu hỏi Là một nhà phát triển ứng dụng, bạn hầu như sẽ bắt gặp chúng trong chữ ký phương thức của các thư viện được viết bằng Objective-C, không có khả năng thể hiện các loại tùy chọn.

Từ việc sử dụng Swift với ca cao và Objective-C, phần Làm việc với nil :

Vì Objective-C không đảm bảo rằng một đối tượng không phải là con số không, Swift tạo ra tất cả các lớp trong các loại đối số và các kiểu trả về tùy chọn trong các API Objective-C đã nhập. Trước khi bạn sử dụng một đối tượng Objective-C, bạn nên kiểm tra để đảm bảo rằng nó không bị thiếu.

Trong một số trường hợp, bạn có thể hoàn toàn chắc chắn rằng một phương thức hoặc thuộc tính Objective-C không bao giờ trả về một niltham chiếu đối tượng. Để làm cho các đối tượng trong kịch bản đặc biệt này thuận tiện hơn để làm việc với, Swift nhập các loại đối tượng dưới dạng tùy chọn không được bao bọc . Các loại tùy chọn không được bao hàm hoàn toàn bao gồm tất cả các tính năng an toàn của các loại tùy chọn. Ngoài ra, bạn có thể truy cập giá trị trực tiếp mà không cần kiểm tranilhoặc tự mở nó ra. Khi bạn truy cập giá trị trong loại tùy chọn này mà không hủy kết nối nó một cách an toàn trước, thì việc bỏ chọn tùy chọn ngầm định xem giá trị có bị thiếu hay không. Nếu giá trị bị thiếu, lỗi thời gian chạy xảy ra. Do đó, bạn phải luôn luôn tự kiểm tra và mở khóa một tùy chọn hoàn toàn chưa được mở, trừ khi bạn chắc chắn rằng giá trị không thể thiếu.

... và xa hơn nữa nằm ở đây con rồng


Cảm ơn câu trả lời thấu đáo này. Bạn có thể nghĩ ra một danh sách kiểm tra nhanh về thời điểm sử dụng một Tùy chọn chưa được ẩn hoàn toàn và khi nào một biến tiêu chuẩn sẽ đủ?
Hairgami_Master

@Hairgami_Master Tôi đã thêm câu trả lời của riêng mình bằng một danh sách và ví dụ cụ thể
drewag

18

Các ví dụ đơn giản một dòng (hoặc vài dòng) không thể hiện rất rõ hành vi của các tùy chọn - vâng, nếu bạn khai báo một biến và cung cấp cho nó một giá trị ngay lập tức, sẽ không có điểm nào trong một tùy chọn.

Trường hợp tốt nhất tôi thấy cho đến nay là thiết lập xảy ra sau khi khởi tạo đối tượng, tiếp theo là sử dụng "được bảo đảm" để tuân theo thiết lập đó, ví dụ như trong trình điều khiển xem:

class MyViewController: UIViewController {

    var screenSize: CGSize?

    override func viewDidLoad {
        super.viewDidLoad()
        screenSize = view.frame.size
    }

    @IBAction printSize(sender: UIButton) {
        println("Screen size: \(screenSize!)")
    }
}

Chúng tôi biết printSizesẽ được gọi sau khi chế độ xem được tải - đó là một phương thức hành động được nối với một điều khiển bên trong chế độ xem đó và chúng tôi đảm bảo không gọi nó bằng cách khác. Vì vậy, chúng ta có thể tiết kiệm cho mình một số tùy chọn kiểm tra / ràng buộc với !. Swift không thể nhận ra sự đảm bảo đó (ít nhất là cho đến khi Apple giải quyết vấn đề tạm dừng), vì vậy bạn nói với trình biên dịch rằng nó tồn tại.

Điều này phá vỡ sự an toàn ở một mức độ nào đó, mặc dù. Bất cứ nơi nào bạn có tùy chọn không được bao bọc hoàn toàn là nơi ứng dụng của bạn có thể bị sập nếu "bảo đảm" của bạn không luôn luôn giữ, vì vậy đây là một tính năng để sử dụng một cách tiết kiệm. Bên cạnh đó, việc sử dụng !tất cả thời gian làm cho âm thanh giống như bạn đang la hét và không ai thích điều đó.


Tại sao không chỉ khởi tạo screenSize thành CGSize (height: 0, width: 0) và lưu lại rắc rối khi phải hét biến mỗi khi bạn truy cập nó?
Martin Gordon

Một kích thước có thể không phải là ví dụ tốt nhất, vì CGSizeZerocó thể là một giá trị trọng tâm tốt trong sử dụng trong thế giới thực. Nhưng nếu bạn có một kích thước được tải từ ngòi mà thực sự có thể bằng 0 thì sao? Sau đó, sử dụng CGSizeZeronhư một sentinel không giúp bạn phân biệt giữa một giá trị không được đặt và được đặt thành không. Hơn nữa, điều này cũng áp dụng tương tự cho các loại khác được tải từ ngòi (hoặc bất kỳ nơi nào khác sau init): chuỗi, tham chiếu đến các cuộc phỏng vấn, v.v.
rickster

2
Một phần của điểm Tùy chọn trong các ngôn ngữ chức năng là không có các giá trị sentinel. Bạn có một giá trị hoặc bạn không có. Bạn không nên có trường hợp bạn có một giá trị biểu thị giá trị còn thiếu.
Wayne Tanner

4
Tôi nghĩ bạn đã hiểu nhầm câu hỏi của OP. OP không hỏi về trường hợp chung cho các tùy chọn, thay vào đó, cụ thể là về nhu cầu / việc sử dụng các tùy chọn không được bao bọc hoàn toàn (nghĩa là không let foo? = 42, thay vào đó let foo! = 42). Đây không phải là địa chỉ đó. (Đây có thể là một câu trả lời có liên quan về các tùy chọn, làm phiền bạn, nhưng không phải về các tùy chọn không được bao bọc hoàn toàn là một động vật khác / có liên quan.)
JMD

15

Apple đưa ra một ví dụ tuyệt vời trong Ngôn ngữ lập trình Swift -> Đếm tham chiếu tự động -> Giải quyết các chu kỳ tham chiếu mạnh giữa các trường hợp lớp -> Tài liệu tham khảo chưa được đặt tên và các thuộc tính tùy chọn không bị che giấu

class Country {
    let name: String
    var capitalCity: City! // Apple finally correct this line until 2.0 Prerelease (let -> var)
    init(name: String, capitalName: String) {
        self.name = name
        self.capitalCity = City(name: capitalName, country: self)
    }
}

class City {
    let name: String
    unowned let country: Country
    init(name: String, country: Country) {
        self.name = name
        self.country = country
    }
}

Trình khởi tạo cho Cityđược gọi từ bên trong trình khởi tạo cho Country. Tuy nhiên, trình khởi tạo Countrykhông thể chuyển selfđến trình Citykhởi tạo cho đến khi một phiên bản mới Countryđược khởi tạo hoàn toàn, như được mô tả trong Khởi tạo hai pha .

Để đối phó với yêu cầu này, bạn khai báo capitalCitytài sản Countrylà một tài sản tùy chọn không được bao bọc ngầm.


Hướng dẫn được đề cập trong câu trả lời này là ở đây .
Franklin Yu

3

Lý do của các tùy chọn ngầm là dễ dàng hơn để giải thích bằng cách đầu tiên nhìn vào cơ sở lý luận cho việc hủy bỏ bắt buộc.

Buộc hủy bỏ tùy chọn (ẩn hoặc không), bằng cách sử dụng! toán tử, có nghĩa là bạn chắc chắn rằng mã của bạn không có lỗi và tùy chọn đã có giá trị khi nó chưa được mở. Nếu không có! toán tử, có lẽ bạn chỉ cần xác nhận với một ràng buộc tùy chọn:

 if let value = optionalWhichTotallyHasAValue {
     println("\(value)")
 } else {
     assert(false)
 }

cái này không đẹp bằng

println("\(value!)")

Bây giờ, các tùy chọn ngầm cho phép bạn thể hiện có một tùy chọn mà bạn mong muốn luôn có giá trị khi được mở, trong tất cả các luồng có thể. Vì vậy, nó chỉ đi một bước xa hơn trong việc giúp bạn - bằng cách thư giãn yêu cầu viết! để mở khóa mỗi lần và đảm bảo rằng thời gian chạy vẫn sẽ bị lỗi trong trường hợp các giả định của bạn về luồng bị sai.


1
@newacct: non-nil trong tất cả các luồng có thể thông qua mã của bạn không giống như non-nil từ khởi tạo cha mẹ (class / struct) thông qua giao dịch. Trình tạo giao diện là ví dụ cổ điển (nhưng có rất nhiều mẫu khởi tạo bị trì hoãn khác): nếu một lớp chỉ được sử dụng từ ngòi, thì các biến ổ cắm sẽ không được đặt init(mà bạn thậm chí không thể thực hiện), nhưng chúng ' được đảm bảo để được đặt sau awakeFromNib/ viewDidLoad.
còi xương

3

Nếu bạn biết chắc chắn, một giá trị trả về từ một tùy chọn thay vì nil, Tùy chọn không bị ràng buộc sử dụng để bắt trực tiếp các giá trị đó từ các tùy chọn và không phải tùy chọn không thể.

//Optional string with a value
let optionalString: String? = "This is an optional String"

//Declaration of an Implicitly Unwrapped Optional String
let implicitlyUnwrappedOptionalString: String!

//Declaration of a non Optional String
let nonOptionalString: String

//Here you can catch the value of an optional
implicitlyUnwrappedOptionalString = optionalString

//Here you can't catch the value of an optional and this will cause an error
nonOptionalString = optionalString

Vì vậy, đây là sự khác biệt giữa việc sử dụng

let someString : String! let someString : String


1
Điều này không trả lời câu hỏi của OP. OP biết một tùy chọn hoàn toàn không bị ràng buộc là gì.
Franklin Yu

0

Tôi nghĩ Optionallà một tên xấu cho cấu trúc này gây nhầm lẫn cho nhiều người mới bắt đầu.

Các ngôn ngữ khác (ví dụ như Kotlin và C #) sử dụng thuật ngữ này Nullablevà nó giúp việc hiểu điều này trở nên dễ dàng hơn rất nhiều.

Nullablecó nghĩa là bạn có thể gán giá trị null cho một biến thuộc loại này. Vì vậy, nếu đó là Nullable<SomeClassType>, bạn có thể gán null cho nó, nếu chỉ SomeClassType, bạn không thể. Đó chỉ là cách Swift hoạt động.

Tại sao nên sử dụng chúng? Vâng, đôi khi bạn cần phải có null, đó là lý do tại sao. Ví dụ: khi bạn biết rằng bạn muốn có một trường trong một lớp, nhưng bạn không thể gán nó cho bất cứ điều gì khi bạn đang tạo một thể hiện của lớp đó, nhưng sau này bạn sẽ làm. Tôi sẽ không đưa ra ví dụ, bởi vì mọi người đã cung cấp chúng ở đây. Tôi chỉ viết cái này để đưa 2 xu của tôi.

Btw, tôi khuyên bạn nên xem cách thức hoạt động của các ngôn ngữ khác, như Kotlin và C #.

Đây là một liên kết giải thích tính năng này trong Kotlin: https://kotlinlang.org/docs/reference/null-squil.html

Các ngôn ngữ khác, như Java và Scala đều có Optionals, nhưng chúng hoạt động khác với Optionals trong Swift, bởi vì các loại của Java và Scala đều không thể mặc định theo mặc định.

Nói chung, tôi nghĩ rằng tính năng này nên được đặt tên Nullabletrong Swift, và không Optional...


0

Implicitly Unwrapped Optionallà một đường cú pháp cho Optionalđiều đó không buộc một lập trình viên mở khóa một biến. Nó có thể được sử dụng cho một biến không thể khởi tạo trong thời gian two-phase initialization processngụ ý không phải là con số không. Biến này hoạt động như không phảicon số không nhưng thực sự là một biến tùy chọn . Một ví dụ điển hình là - Các cửa hàng của Trình tạo giao diện

Optional thường là thích hợp hơn

var nonNil: String = ""
var optional: String?
var implicitlyUnwrappedOptional: String!

func foo() {
    //get a value
    nonNil.count
    optional?.count

    //Danderour - makes a force unwrapping which can throw a runtime error
    implicitlyUnwrappedOptional.count

    //assign to nil
//        nonNil = nil //Compile error - 'nil' cannot be assigned to type 'String'
    optional = nil
    implicitlyUnwrappedOptional = nil
}
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.