Contents
Chào mừng bạn đến với Fx Studio. Bài viết lần này sẽ là về chủ đề kiến thức cơ bản trong Swift. Đó là Optional. Đây là một thiếu sót của mình, khi quên viết một bài về nó. Bài viết này phù hợp với mọi lứa tuổi, dễ chơi … dễ trúng thưởng. Quan trọng là bạn sẽ pass phỏng vấn được là nhờ nó.
Nếu mọi việc đã ổn rồi, thì …
Bắt đầu thôi!
Chuẩn bị
Optional là một kiến thức siêu cơ bản. Bạn có thể tìm thấy trong bài viết Basic Swift. Đó là bài viết hướng dẫn cách dùng Swift cơ bản. Còn nếu bạn có hứng thú hơn nữa với iOS, thì hãy bắt đầu với seri huyền thoại …
Về tools & version, bạn khỏi cần phải chuẩn bị. Nó ở sẵn trong core của Swift từ những ngày đầu ra mắt rồi. Về mặt demo, bạn chỉ cần sử dụng Playground để thử code cũng ổn à. Thôi vào việc chính nào!
Optional là gì?
Optional là một tính năng cho phép biến hoặc hằng số có thể không chứa giá trị nào. Điều này có nghĩa là:
Trong Swift, một biến hoặc hằng số có thể được khai báo mà không cần phải được gán giá trị ngay lập tức.
Nếu một biến hoặc hằng số được khai báo như một Optional và không được gán giá trị, nó sẽ tự động có giá trị là nil
.
var optionalString: String? // Khai báo một biến Optional optionalString = "Hello, World!" // Gán giá trị cho biến Optional
Trong ví dụ trên, optionalString
là một biến Optional. Nếu bạn không gán giá trị cho nó, giá trị mặc định của nó sẽ là nil
.
Optional có phải là kiểu dữ liệu hay không?
Câu trả lời là Không.
Optional không phải là một kiểu dữ liệu riêng biệt trong Swift. Thay vào đó, nó là một tính năng của ngôn ngữ lập trình Swift cho phép bạn biểu thị rằng một biến hoặc hằng số có thể không chứa giá trị nào.
Khi bạn khai báo một biến hoặc hằng số như một Optional, Swift sẽ tạo ra một phiên bản đặc biệt của kiểu dữ liệu gốc, được gọi là “Optional”. Ví dụ, nếu bạn có một biến String?
, kiểu dữ liệu của nó là “Optional String”, không phải “String”. Điều này có nghĩa là biến có thể chứa một chuỗi hoặc không chứa gì cả (nil
).
Tuy nhiên, bạn không thể tạo ra một kiểu dữ liệu Optional mới. Chỉ có thể sử dụng Optional với các kiểu dữ liệu đã tồn tại trong Swift.
Vì sao có Optional trong Swift?
Optional trong Swift được thiết kế để giải quyết vấn đề liên quan đến việc sử dụng null
trong nhiều ngôn ngữ lập trình khác. Trong nhiều ngôn ngữ, null
có thể được gán cho bất kỳ biến nào, và nếu bạn cố gắng truy cập một giá trị từ một biến null
, bạn sẽ gặp lỗi runtime, thường được gọi là lỗi null pointer.
Swift giải quyết vấn đề này bằng cách yêu cầu bạn rõ ràng khai báo khi một biến có thể không chứa giá trị nào (được gọi là Optional). Điều này giúp bạn tránh được lỗi null pointer, vì bạn phải xử lý trường hợp nil
trước khi bạn có thể sử dụng biến.
Ngoài ra, Swift cung cấp các cơ chế như optional binding và optional chaining để làm việc với chúng một cách an toàn và dễ dàng hơn.
Đọc thêm bài nil & null để hiểu hơn nhóe!
Khai báo
Để khai báo một biến hoặc hằng số với Optional trong Swift, bạn sử dụng dấu hỏi chấm (?
) sau kiểu dữ liệu. Điều này biểu thị rằng biến hoặc hằng số có thể chứa giá trị của kiểu dữ liệu đó hoặc có thể không chứa giá trị nào (nil
).
Dưới đây là một số ví dụ về việc khai báo biến và hằng số:
var optionalInt: Int? // Khai báo một biến Optional kiểu Int let optionalString: String? // Khai báo một hằng số Optional kiểu String optionalInt = 10 // Gán giá trị cho biến Optional print(optionalInt) // In ra: Optional(10) optionalInt = nil // Gán giá trị nil cho biến Optional print(optionalInt) // In ra: nil
Trong ví dụ trên, optionalInt
và optionalString
đều là Optional. optionalInt
có thể chứa một số nguyên hoặc nil
, và optionalString
có thể chứa một chuỗi hoặc nil
.
Khi khai báo một biến Optional mà không gán giá trị ban đầu cho nó. Thì nó sẽ nhận giá trị là
nil
.
Ưu & Nhược
Optional trong Swift có một số ưu điểm và nhược điểm:
Ưu điểm
-
An toàn: giúp ngăn chặn lỗi null pointer, một lỗi phổ biến trong nhiều ngôn ngữ lập trình khác. Bạn phải xử lý trường hợp
nil
trước khi bạn có thể sử dụng biến. -
Rõ ràng: Khi một biến được khai báo như một Optional, bạn biết rằng nó có thể không chứa giá trị nào. Điều này giúp mã của bạn trở nên rõ ràng hơn.
-
Tiện lợi: Swift cung cấp nhiều cơ chế, như optional binding và optional chaining, để làm việc một cách an toàn và tiện lợi.
Nhược điểm
-
Phức tạp: Việc sử dụng có thể làm cho mã của bạn trở nên phức tạp hơn, đặc biệt nếu bạn phải xử lý nhiều lớp Optional.
-
Khó hiểu: Đối với những người mới học Swift, thì có thể khó hiểu. Việc hiểu rõ cách sử dụng Optional đúng cách có thể mất một thời gian.
-
Lỗi runtime: Nếu bạn cố gắng forced unwrap một Optional mà giá trị của nó là
nil
, chương trình của bạn sẽ gặp lỗi runtime.
Cách sử dụng Optional
Để sử dụng một biến Optional trong Swift, bạn cần phải “unwrap” nó, tức là lấy giá trị bên trong nếu nó không phải là nil
. Có hai cách chính để unwrap: “forced unwrapping” và “optional binding“.
Forced unwrapping
Bạn có thể sử dụng dấu chấm than (!
) để unwrap một Optional. Tuy nhiên, nếu bạn cố gắng forced unwrap mà giá trị của nó là nil
, chương trình của bạn sẽ gặp lỗi runtime.
var optionalInt: Int? = 10 print(optionalInt!) // In ra: 10
Trong dự án thực tế thì sẽ không khuyến khích dùng cách này.
Optional binding
Đây là cách an toàn hơn để unwrap. Bạn có thể sử dụng cấu trúc if let
hoặc guard let
để kiểm tra xem có giá trị hay không trước khi unwrap.
var optionalInt: Int? = 10 if let value = optionalInt { print(value) // In ra: 10 } else { print("optionalInt is nil") }
Trong ví dụ trên, nếu optionalInt
có giá trị, nó sẽ được gán cho value
và code trong khối if
sẽ được thực thi. Nếu optionalInt
là nil
, code trong khối else
sẽ được thực thi.
Implicitly Unwrapped Optionals
Đây là một kết hợp giữa việc khai báo & sử dụng cùng nhau.
Implicitly Unwrapped Optionals là một dạng khác của nó. Khi bạn khai báo một Implicitly Unwrapped Optional, bạn đang nói với Swift rằng biến hoặc hằng số này ban đầu có thể là nil
. Nhưng một khi nó được gán giá trị, nó sẽ luôn có giá trị từ đó về sau.
Để khai báo một Implicitly Unwrapped Optional, bạn sử dụng dấu chấm than (!
) thay vì dấu hỏi chấm (?
) sau kiểu dữ liệu.
var implicitlyUnwrappedOptionalInt: Int! // Khai báo một Implicitly Unwrapped Optional kiểu Int
Khi bạn truy cập một Implicitly Unwrapped Optional, Swift sẽ tự động unwrap nó cho bạn. Tuy nhiên, nếu giá trị của nó là nil
khi bạn cố gắng truy cập, chương trình của bạn sẽ gặp lỗi runtime.
implicitlyUnwrappedOptionalInt = 10 print(implicitlyUnwrappedOptionalInt) // In ra: 10 implicitlyUnwrappedOptionalInt = nil print(implicitlyUnwrappedOptionalInt) // Lỗi runtime: unexpectedly found nil while unwrapping an Optional value
Vì vậy, bạn chỉ nên sử dụng cách này, khi bạn chắc chắn rằng chúng sẽ không bao giờ là nil
khi bạn truy cập chúng.
Các toán tử sử dụng với Optional
Có một số toán tử được sử dụng cụ thể như sau:
Optional Chaining (?.)
Đây là một cách để truy cập các thuộc tính, phương thức, hoặc chỉ số của một Optional mà không cần phải unwrap nó trước. Nếu nó chứa giá trị, thì các hoạt động sẽ được thực hiện và trả về kết quả dưới dạng Optional. Nếu nó là nil
, thì toàn bộ biểu thức sẽ trả về nil
.
let optionalString: String? = "Hello, World!" let count = optionalString?.count // count là một Int Optional
Nil-Coalescing Operator (??)
Đây là một cách để giải quyết một Optional. Nếu nó chứa giá trị, nó sẽ trả về giá trị đó. Nếu nó là nil
, nó sẽ trả về giá trị mặc định bạn cung cấp.
let optionalInt: Int? = nil let value = optionalInt ?? 0 // value là 0
Optional Binding (if let hoặc guard let)
Đây là một cách an toàn để unwrap một Optional. Nếu nó chứa giá trị, nó sẽ được gán cho một biến tạm thời và code trong khối if
hoặc guard
sẽ được thực thi.
var optionalInt: Int? = 10 if let value = optionalInt { print(value) // In ra: 10 }
Forced Unwrapping (!)
Đây là một cách để unwrap một Optional. Tuy nhiên, nếu bạn cố gắng forced unwrap một Optional mà giá trị của nó là nil
, chương trình của bạn sẽ gặp lỗi runtime.
var optionalInt: Int? = 10 print(optionalInt!) // In ra: 10
Nested optionals (!!)
Trong Swift, toán tử !!
không phải là một toán tử hợp lệ. Tuy nhiên, nếu bạn thấy !!
trong mã Swift, đó có thể là việc sử dụng hai toán tử !
liên tiếp.
Toán tử !
được sử dụng để “forced unwrap” một Optional. Nếu bạn có một biến Optional và bạn chắc chắn rằng nó không phải là nil
, bạn có thể sử dụng !
để unwrap nó và truy cập giá trị bên trong.
Ví dụ:
var name: String? = "Antoine van der Lee" print(name!.count) // In ra: 17
let nameAndAges: [String:Int?] = ["Antoine van der Lee": 28] let antoinesAge = nameAndAges["Antoine van der Lee"] print(antoinesAge) // Prints: "Optional(Optional(28))" print(antoinesAge!) // Prints: "Optional(28)" print(antoinesAge!!) // Prints: "28"
Trong ví dụ trên, name
là một Optional String. Khi chúng ta sử dụng !
để unwrap name
, chúng ta có thể truy cập phương thức count
của String bên trong. Tuy nhiên, giá trị của nó là nil
, chương trình của bạn sẽ gặp lỗi runtime. Vì vậy, nếu bạn thấy !!
trong mã Swift, đó có thể là việc sử dụng hai lần forced unwrap.
Điều này chỉ nên được sử dụng khi bạn chắc chắn rằng Optional không phải là
nil
.
Trong trường hợp của bạn, name
là một Double Optional (String??
), nghĩa là nó là một Optional bên trong một Optional khác. Sử dụng !!
sẽ unwrap cả hai lớp Optional. Tuy nhiên, điều này có thể gây ra lỗi runtime nếu bất kỳ lớp Optional nào là nil
.
Nên & Không nên
Nên
Khi làm việc có một số điều bạn nên lưu ý:
-
Tránh Forced Unwrapping: Nếu bạn không chắc chắn rằng một Optional chứa giá trị, bạn nên tránh sử dụng
!
để forced unwrap nó. Điều này có thể dẫn đến lỗi runtime nếu Optional lànil
. -
Sử dụng Optional Binding: Đây là một cách an toàn để unwrap. Nó cho phép bạn kiểm tra xem có giá trị hay không và gán giá trị đó cho một biến tạm thời nếu có.
if let unwrappedValue = optionalValue { print(unwrappedValue) }
-
Sử dụng Optional Chaining: Đây là một cách tiện lợi để truy cập các thuộc tính, phương thức và subscript của một Optional mà không cần phải unwrap nó.
let count = optionalArray?.count
-
Sử dụng Nil-Coalescing Operator: Toán tử
??
cho phép bạn cung cấp một giá trị mặc định cho một Optional. Nếu nó lànil
, giá trị mặc định sẽ được sử dụng.
let value = optionalValue ?? defaultValue
-
Sử dụng Optional trong các tham số hàm: Nếu một tham số hàm có thể không có giá trị, bạn nên khai báo nó như một Optional. Điều này cho phép bạn gọi hàm mà không cần cung cấp giá trị cho tham số đó.
func optionalParameterFunction(optionalParameter: Int?) { // ... }
-
Sử dụng Optional trong các thuộc tính: Nếu một thuộc tính của một lớp hoặc cấu trúc có thể không có giá trị.
Không nên
Khi làm việc có một số điều bạn nên tránh:
-
Tránh Forced Unwrapping: Nếu bạn không chắc chắn rằng một Optional chứa giá trị, bạn nên tránh sử dụng
!
để forced unwrap nó. -
Tránh sử dụng Optional khi không cần thiết: Nếu bạn biết rằng một biến sẽ luôn luôn có giá trị và không bao giờ là
nil
, bạn nên khai báo nó như một non-optional. Sử dụng nhiều khi không cần thiết, có thể làm cho mã của bạn trở nên phức tạp hơn. -
Tránh sử dụng Optional cho các giá trị mà không bao giờ là
nil
: Điều này có thể gây nhầm lẫn và làm cho mã của bạn trở nên khó hiểu hơn. -
Tránh không xử lý trường hợp
nil
: Khi làm việc với Optional, bạn nên luôn xử lý trường hợpnil
. -
Tránh sử dụng Optional chaining quá nhiều: Dù Optional chaining là một tính năng tiện lợi, nhưng nếu sử dụng quá nhiều, mã của bạn có thể trở nên khó đọc và khó hiểu.
Optional trong Class
Một ví dụ đẹp và rất sướng!
class Person { var name: String? var age: Int? var address: String? }
Khi khai báo một class trong Swift. Không nên khai báo tất cả các thuộc tính là Optional trừ khi bạn thực sự cần chúng có thể không chứa giá trị. Việc sử dụng Optional nên dựa trên yêu cầu logic của chương trình.
- Nếu một thuộc tính luôn luôn có giá trị trong suốt vòng đời của một đối tượng. Thì nó nên được khai báo như một non-optional.
- Nếu một thuộc tính có thể không có giá trị tại một thời điểm nào đó. Thì nó nên được khai báo như một Optional.
Việc sử dụng Optional một cách không cần thiết có thể làm cho mã của bạn trở nên phức tạp hơn và khó hiểu hơn. Bạn sẽ phải unwrap các giá trị Optional mỗi khi bạn muốn sử dụng chúng, và bạn cũng phải xử lý trường hợp nil
.
Dưới đây là một ví dụ về cách khai báo một class trong Swift với một số thuộc tính là Optional:
class Person { var name: String // Non-optional var age: Int // Non-optional var address: String? // Optional init(name: String, age: Int) { self.name = name self.age = age } }
Trong ví dụ trên, name
và age
là non-optional vì mỗi người đều phải có tên và tuổi. Tuy nhiên, address
là Optional vì không phải mọi người đều có địa chỉ.
Optional là một Enum
Trong Swift, Optional thực sự là một Enum. Đây là cách nó được khai báo trong thư viện chuẩn của Swift:
enum Optional<Wrapped>: ExpressibleByNilLiteral { case none case some(Wrapped) }
Optional
là một Enum với hai trường hợp: none
và some(Wrapped)
. none
tương ứng với nil
, và some(Wrapped)
chứa giá trị thực sự.
Khi bạn khai báo một biến Optional, Swift sẽ tự động gán giá trị none
cho nó:
var optionalInt: Int? // Giá trị mặc định là .none
Khi bạn gán một giá trị cho biến Optional, Swift sẽ đóng gói giá trị đó vào trong some(Wrapped)
:
optionalInt = 10 // Giá trị hiện tại là .some(10)
Và khi bạn gán nil
cho biến Optional, Swift sẽ gán none
cho nó:
optionalInt = nil // Giá trị hiện tại là .none
Điều này giúp Swift xử lý nil
một cách an toàn hơn so với nhiều ngôn ngữ lập trình khác. Bạn không thể truy cập giá trị của một biến Optional mà không xử lý trường hợp nil
trước.
Ví dụ code khác:
// ---------- // enum Optional<Wrapped> { /// The absence of a value. case none /// The presence of a value, stored as `Wrapped`. case some(Wrapped) } // ---------- // let name = Optional.some("Antoine van der Lee") print(name!.count) // ---------- // func printName(_ name: String?) { switch name { case .some(let unwrappedValue): print("Name is \(unwrappedValue)") case .none: print("Name is nil") } } printName(nil) // Prints: "Name is nil" printName("Antoine van der Lee") // Prints: "Name is Antoine van der Lee"
Extending optionals
Trong Swift, bạn có thể mở rộng (extend) giống như bất kỳ kiểu dữ liệu nào khác. Điều này cho phép bạn thêm các phương thức và thuộc tính mới vào kiểu Optional. Dưới đây là một ví dụ:
extension Optional where Wrapped == String { func isNullOrEmpty() -> Bool { switch self { case .some(let value): return value.isEmpty case .none: return true } } } var optionalString: String? = "Hello, World!" print(optionalString.isNullOrEmpty()) // In ra: false optionalString = "" print(optionalString.isNullOrEmpty()) // In ra: true optionalString = nil print(optionalString.isNullOrEmpty()) // In ra: true
Trong ví dụ trên, một phương thức mới isNullOrEmpty()
được thêm vào kiểu Optional khi Wrapped
là String
. Phương thức này kiểm tra xem giá trị bên trong biến có phải là chuỗi rỗng hoặc nil
không.
Can thiệp vào việc xử lý giá trị của Optional
extension Optional where Wrapped == String { //... var orEmpty: String { return self ?? "" } //... } var name: String? = "Antoine van der Lee" print(name.orEmpty) // Prints: "Antoine van der Lee" name = nil print(name.orEmpty) // Prints: ""
Đoạn mã trên mở rộng kiểu, khi kiểu Wrapped
là String
. Nó thêm một thuộc tính tính toán mới orEmpty
vào kiểu Optional.
Thuộc tính orEmpty
trả về giá trị của biến, nếu nó không phải là nil
. Hoặc trả về một chuỗi rỗng (""
) nếu biến là nil
. Điều này được thực hiện bằng cách sử dụng toán tử ??
, một toán tử của Swift cho phép bạn cung cấp một giá trị mặc định cho một biến khi nó là nil
.
Tạm kết
- Khái niệm, ý nghĩa & mục đích
- Khai báo & cách sử dụng. Cũng như các toán liên quan
- Một số điểm cần lưu ý khi dùng trong dự án thực tế
- Một số điểm tương tác nâng cao hơn
Tham khảo:
Okay! Tới đây, mình xin kết thúc bài viết giới thiệu về Optional. Nếu có gì thắc mắc hay góp ý cho mình. Thì bạn có thể để lại bình luận hoặc gửi email theo trang Contact.
Cảm ơn bạn đã đọc bài viết này!
Related Posts:
Written by chuotfx
Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!
Leave a Reply Cancel reply
Fan page
Tags
Recent Posts
- Charles Proxy – Phần 1 : Giới thiệu, cài đặt và cấu hình
- Complete Concurrency với Swift 6
- 300 Bài code thiếu nhi bằng Python – Ebook
- Builder Pattern trong 10 phút
- Observer Pattern trong 10 phút
- Memento Pattern trong 10 phút
- Strategy Pattern trong 10 phút
- Automatic Reference Counting (ARC) trong 10 phút
- Autoresizing Masks trong 10 phút
- Regular Expression (Regex) trong Swift
You may also like:
Archives
- September 2024 (1)
- July 2024 (1)
- June 2024 (1)
- May 2024 (4)
- April 2024 (2)
- March 2024 (5)
- January 2024 (4)
- February 2023 (1)
- January 2023 (2)
- November 2022 (2)
- October 2022 (1)
- September 2022 (5)
- August 2022 (6)
- July 2022 (7)
- June 2022 (8)
- May 2022 (5)
- April 2022 (1)
- March 2022 (3)
- February 2022 (5)
- January 2022 (4)
- December 2021 (6)
- November 2021 (8)
- October 2021 (8)
- September 2021 (8)
- August 2021 (8)
- July 2021 (9)
- June 2021 (8)
- May 2021 (7)
- April 2021 (11)
- March 2021 (12)
- February 2021 (3)
- January 2021 (3)
- December 2020 (3)
- November 2020 (9)
- October 2020 (7)
- September 2020 (17)
- August 2020 (1)
- July 2020 (3)
- June 2020 (1)
- May 2020 (2)
- April 2020 (3)
- March 2020 (20)
- February 2020 (5)
- January 2020 (2)
- December 2019 (12)
- November 2019 (12)
- October 2019 (19)
- September 2019 (17)
- August 2019 (10)