Optional trong Swift

Untitled-1
Trước khi nói về Optional, chúng ta sẽ nói sơ lược về nil. Trong Objective-C, nil là con trỏ trỏ tới một object không tồn tại. Và khi chúng ta sử dụng một biến có giá trị nil đôi khi sẽ khiến ứng dụng bị crash. Trong Swift, nil không phải là con trỏ, cũng chẳng phải một primitive, mà nó là sự quy ước thể hiện sự vắng mặt giá trị của một kiểu dữ liệu nào đó.


Optional là gì ?

Nói một cách vắn tắt, Optional là một tính năng rất mạnh mẽ của Swift để giúp chương trình trở nên an toàn và ít bị crash hơn.
Optional hoạt động dựa trên nguyên tắc sự vắng mặt giá trị. Chúng ta sử dụng Optional cho một biến hoặc hằng nào đó khi biến/hằng đó có thể có giá trị hoặc không. Nói nôm na, chúng ta có thể xem biến/hằng là trái táo, thì Optional giống như một cái hộp đựng táo, sẽ có hai khả năng xảy ra khi chúng ta mở hộp, một là trong hộp có táo, hai là trong hộp chả có gì cả.
Kí hiệu của Optional là Optional với T là kiểu dữ liệu, để ngắn gọn hơn người ta dùng kiểu viết tắt là T?

1

Một biến có kiểu Optional (hay Int?) có nghĩa là biến đó có thể có giá trị là một số nguyên, hoặc là nil. 
Còn một biến có kiểu Int thì chỉ có thể có giá trị là một số nguyên.


Đặc tính

  • Optional cho phép chúng ta gán biến bằng nil.
1_1

Đối với trường hợp không phải Optional, chúng ta không thể gán biến bằng nil:

1_2
  • Khi không đặt giá trị mặc định, biến Optional có giá trị khởi tạo là nil.
1_3

Khi không đặt giá trị mặc định, biến không phải Optional không có giá trị khởi tạo nào cả.


Force Unwrapping (!)

Khi chúng ta khai báo và sau đó sử dụng biến Optional, compiler sẽ ngay lập tức báo lỗi:

2

Vì lúc này biến d có kiểu là Int? chứ không phải Int, chúng ta muốn lấy giá trị Int của d để tính toán thì phải làm sao? Điều này đẫn đến khái niệm force unwrapping.

Force unwrapping là việc chúng ta cam đoan với trình biên dịch rằng một biến optional nào đó CHẮC CHẮN CÓ GIÁ TRỊ, giống như việc chúng ta mở hộp táo (unwrapping) khi đoan chắc trong hộp đó có táo vậy.

Để force unwrapping một biến Optional, ta sử dụng dấu ! phía sau biến đó.

3

Một cách an toàn hơn, ta có thể lồng thêm vòng if như sau:

4

Một ví dụ khác với biến String:

5

Sau khi chạy đoạn code trên, biến surveyAnswer có kiểu dữ liệu là Optional nên có giá trị là Optional(“No”). Để lấy được giá trị thực của surveyAnswer, vì chúng ta biết chắc chắn là surveyAnswer có giá trị nên chúng ta sẽ unwrap nó:

6


Optional Binding

Cũng như forced unwrapping, optional binding cũng là một cách để “mở hộp táo”. Chúng ta sử dụng optional binding để kiểm tra xem một optional có chứa giá trị hay không, nếu có thì unwrap và gán giá trị đó vào 1 biến tạm hoặc hằng tạm.
Optional binding có thể sử dụng với từ khoá if hoặc while, thông dụng hơn cả là if letif var.

7

Ở ví dụ trên, nếu biến surveyAnswer có giá trị thì gán giá trị đó cho hằng unwrappedSurveyAnswer. Hằng unwrappedSurveyAnswer này chỉ dùng được trong scope if. Ngược lại nếu biến surveyAnswer không có giá trị thì thực hiện code trong scope else.


Implicitly Unwrapped Optional

Với một biến kiểu Optional, muốn dùng nó ta phải forced unwrapping (hoặc optional binding), dùng bao nhiêu lần thì phải forced unwrapping bấy nhiêu lần. Đôi khi một biến ta biết có giá trị rồi mà cứ phải forced unwrapping nhiều lần thì cũng hơi phiền phức. Implicitly Unwrapped Optional sẽ giúp chúng ta giải quyết phiền phức này, ta chỉ cần forced unwrapping ngay lúc khởi tạo biến, rồi sau đó sử dụng thoải mái mà không cần phải forced unwrapping nữa.

Chúng ta sử dụng Implicitly Unwrapped Optional cho một biến hoặc hằng nào đó khi biến/hằng đó chúng ta biết sẽ luôn luôn có giá trị và không bao giờ bị nil. Nói nôm na giống như một cái giỏ táo được đảm bảo sẽ có táo.

Kí hiệu của Implicitly Unwrapped Optional là ImplicitlyUnwrappedOptional với T là kiểu dữ liệu, để ngắn gọn hơn người ta dùng kiểu viết tắt là T!

8


Optional Chaining

Để hiểu Optional Chaining là gì chúng ta xem qua ví dụ sau:

9

Ta có một extension của class Int với 3 phương thức add, multiply và divide. Ta thực hiện một phép toán đơn giản number* 5 + 1 với biến number như sau:

10

Bây giờ ta thực hiện phép toán number/0 + 1.

11

Do kiểu trả về của hàm divide là Int? nên ta cần kiểm tra khác nil và unwrap rồi mới gọi hàm add.
Thế nhưng nếu gặp phép toán number/2/3/4 + 1 thì sao, nếu vẫn dùng cách trên thì code của chúng ta sẽ rất nhiều dòng chỉ để thực hiện phép tính toán đơn giản. Vì thế mà Optional Chaining ra đời. Với việc sử dụng Optional Chaining, đoạn code thực hiện phép toán number/0 + 1 sẽ như sau:

12

Lúc này, nếu kết quả trả về của hàm divide là nil thì không quan tâm các hàm gọi sau nó nữa, trả về kết quả là nil luôn. Ngược lại nếu hàm divide trả về có giá trị thì unwrap để lấy giá trị đó và gọi tiếp các hàm ở sau.

Optional Chaining không chỉ áp dụng cho gọi lồng hàm như trên mà còn áp dụng cho truy vấn property và subscript.

Như vậy, Optional Chaining là một quá trình xử lý khi chúng ta gọi các hàm, truy cập các properties và subscripts của một optional. Quá trình xử lý đó là: Khi optional có giá trị, việc gọi hàm, truy cập property, subscript thành công và không có gì đáng nói. Khi optional đó bị nil, kết quả trả về của việc gọi hàm, truy cập property, subscript cũng là nil. Quá trình gọi, truy cập như trên có thể lồng nhau, và chỉ cần một lời gọi, truy cập trả về nil thì kết quả tổng thể cũng trả về nil ngay và luôn.


Kết luận

Có quá nhiều thuật ngữ, tính năng liên quan đến optional, vậy khi code phải dùng cái nào? Chúng ta chỉ cần nhớ:
  • Trường hợp một biến nào đó chúng ta chắc chắn nó luôn có giá trị, không thể nil được thì chúng ta khai báo biến đó với kiểu Implicitly Unwrapped Optional.
  • Trường hợp ngược lại, biến có thể bị nil, chúng ta khai báo với kiểu Optional. Sau đó tuỳ tình huống mà ta sẽ sử dụng Force Unwrapping hay Optional Binding.
  • Dùng Optional Chaining để code được gọn và an toàn hơn.
Chúc các bạn code trong hạnh phúc! Happy coding!


Tham khảo

  1. The Swift Programming Languages.
  2. https://niviki.com/toan-tap-ve-optional-trong-swift/
  3. http://dev.ethanify.me/swift/swift-optionals-2

Nhận xét

Bài đăng phổ biến từ blog này

Property trong Objective-C

Quản lý bộ nhớ trong Objective-C