Contents
Chào mừng bạn đến với Fx Studio. Chủ đề bài viết lần này là hai khái niệm kinh điển trong Swift lẫn Objective-C, đó là nil & null. Hay còn gọi là Nullability. Chúng ta khám phá chúng thông qua các bài học thực tiễn rút ra từ quá trình phát triển các ứng dụng iOS cho cả 2 ngôn ngữ.
Nếu bạn chưa biết về Objective-C, thì cũng không cần lo lắng về nó. Bạn vẫn áp dụng những kiến thức từ Swift sang Objective-C một cách mượt mà nhóe! Còn nếu bạn chưa biết gì về Swift, thì bạn có thể bắt đầu tìm hiểu về Swift qua loạt bài viết này.
Còn nếu mọi việc đã ổn rồi, thì …
Bắt đầu thôi!
Nullability trong Objective-C & Swift
Đây là một trong những khái niệm rất linh động khi chúng ta sử dụng với Objective-C. Nếu bạn là một lập trình viên Objective-C rồi thì cũng khá quen với cụng từ này.
messages can be sent to nil.
Điều này có nghĩa nil sẽ sử dụng được, sẽ truyền đi trong các lời gọi hàm. Đủ an toàn để không bị crash chương trình.
Còn với Swift, chúng ta sẽ được đảm bảo an toàn nhiều hơn. Vì nil của bạn chỉ sử dụng được trong các kiểu dữ liệu Optional. Và chúng ta vẫn có thể truyền đi “messeages to nil”. Và cũng chính từ thực tế chục năm code thuê của mình, thì với nil hay nullability với:
- Objevtive-C thật là một mớ hỗn độn.
- Swift là cầu nỗi giữa các kiểu dữ liệu khác nhau.
Ở đây, chúng ta sẽ có vài bài học từ thực tế để cho bạn khám phá thế giới hư ảo này.
nil – kẻ hủy diệt
Các lập trình viên từ Objective-C tới Swift thì hầu như đã quen thuộc tới nil rồi. Một đối tượng trỏ tới nil, thì có nghĩa đó là con trỏ nil. Ừ, nghe méo thông não được rồi.
nil về bản chất là hư vô (không tồn tại)
Khi đó chúng ta sẽ không có gì, không có giá trị tồn tại cho đối tượng của chúng ta. Điều này cũng khá thú vị, nhất là với Objective-C. Bạn có thể chưa biết gì về Objective-C, nhưng cũng hãy xem qua ví dụ sau:
NSArray<NSString *> *array = [NSArray arrayWithObjects:@"Alice", @"Eileen", @"Sepia Alice", nil]; for(NSString *string in array) { NSLog(@"%@", string); }
Về bản chất, bạn cũng có thể đoán ra được ý nghĩa của đoạn code rồi. Nhưng điểm đặc biết là chúng ta sử dụng nil ở cuối array, nhằm thông báo một điều.
nil sẽ là kết thúc cho một quá trình
Đây là cách cũ, rất cũ rồi. Bạn sẽ khởi tạo một array như vậy. Đây chính là quá khứ đầy đau thương với Objective-C. Bạn thử nghĩ xem nếu chúng ta có nhiều hơn nil thì sao.
NSArray<NSString *> *array = [NSArray arrayWithObjects:@"Alice", nil, @"Eileen", @"Sepia Alice", nil]; for(NSString *string in array) { NSLog(@"%@", string); }
Bây giời mới thực sự là hỗn loạn. Mọi thức sẽ kết thúc ngay ở vòng lặp đầu tiên rồi. Và khi bạn muốn, chúng ta có một mãng vừa có những phần tử có giá trị và nil thì sẽ như thế nào.
NSNull
Nếu nil là hư vô, thì NSNull là đại diện của hư vô.
Tới đây, bạn sẽ nỗ não luôn rồi. Còn đơn giản hóa thì NSNull có một phương thức là null. Nó có mục đích cung cấp cho bạn một singleton cho một đại diện hay giá trị giữ chỗ (placeholder) là hư vô (không có & không tồn tại giá trị).
Bởi vì bản chất của Cocoa Framework rất linh động. Cũng tùy thuộc vào cách chúng ta dùng chúng, mà có khi dẫn tới mục đích thuần túy sẽ không còn đúng nữa. Ví dụ, bạn sẽ có một câu hỏi là: “Chúng ta có thể có một array các phần tử với giá trị null không?”. Cây trả lời sẽ là vừa có và vừa không.
NSNull thực tế là một đối tượng (object), kế thừa từ NSObject. Trong Objective-C, bạn không thể có một array với nil thuần túy hoặc hư vô thuần túy. Những gì bạn cố gắng là thay thế bằng các đối tượng NSNull mà thôi. NSNull như là một lớp vỏ bảo cho nil.
Ta thử nghiệm tiếp với ví dụ trên nào:
NSArray<NSString *> *array = [NSArray arrayWithObjects:@"Alice", [NSNull null], @"Eileen", @"Sepia Alice", nil];
Kết quả sẽ như sau:
Alice <null> Eileen Sepia Alice
Trông cũng khá ổn rồi đó. Tóm tắt lại, khi bạn:
- Cần tới một hư vô (không tồn tại) thì hãy sử dụng nil
- Còn khi bạn cần thực thi hay sử dụng hư vô (giá trị) thì hãy sử dụng NSNull
NSNull trong Swift
Chuyển sang Swift thì mọi việc đã tươi sáng hơn nhiều. Bạn đã có một kiểu dữ liệu cho những thứ liên quan tới Nullability rồi. Đó là Optional thần thánh.
Với ví dụ trên, khi một array là một Optional, thì các phần tử của nó có thể là nil và nó hoạt động như bao array bình thường khác.
let dolls: [String?] = ["Alice", nil, "Eileen", "Sepia Alice"] for doll in dolls { print(doll) }
Bạn cập nhật lại ví dụ như đoạn code trên với Swift. Trong đó, muốn sử dụng kiểu dữ liệu Optional, thì chúng ta thêm dấu ?
để khai báo cho nó. Kết quả nhận được như sau:
Optional("Alice") nil Optional("Eileen") Optional("Sepia Alice")
Về mặt lý thuyết, array với Optional thì vẫn không phải là nullability thuần túy, nhưng bạn có thể làm việc trực tiếp được với nullability.
nil & null trong Dictionary
Câu chuyện với Dictionary lại phức tạp hơn nữa. Với Nullability ở đây, bạn sẽ rơi vào nhiều trường hợp khác nhau. Và nếu bạn không hiểu về Objective-C & Swift thì rất có thể có nhiều lỗi phát sinh từ đó.
Objective-C
Chúng ta xem qua một file JSON như sau:
{ "Pullip": [ "Classical Alice", "Eileen", "Alice Sepia" ], "Myou": [ "Delia", "Matcha" ], "HarmoniaBloom": null }
Sử dụng NSJSONSerialization để parse file JSON đó thành dictionary với kiểu dữ liệu như sau NSDictionary<NSString*, NSArray<NSString *> *>. Code tạo sẽ như sau:
NSURL *file = [[NSBundle mainBundle] URLForResource:@"dolls" withExtension:@"json"]; NSData *data = [NSData dataWithContentsOfURL:file]; NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
Mọi việc vẫn không có gì phức tạp hết. Tới khi, bạn quyết định sử dụng nó để truy cập các giá trị bên trong. Ví dụ như là:
NSLog(@"%@", dictionary[@"Nendoroid"]); NSLog(@"%@", dictionary[@"HarmoniaBloom"]);
Kết quả nhận được sẽ như sau:
(null) <null>
Console sẽ in ra kết quả với (null) nếu nó thực sự là hư vô (hay nil) và <null> nếu nó là một NSNull. Hay là chúng ta truy cập tới key không có trong dictionary thì kết quả trả về là nil. Còn với key tồn tại rồi nhưng giá trị của nó là null, thì kết quả sẽ là NSNull.
Ở trên, bạn vẫn sử dụng những thứ thuộc về dictionary và chúng vẫn có thể hoạt động tốt. Nhưng khi bạn dùng trực tiếp đối tượng trong Dictionary thì sẽ khác. Ví dụ như sau:
//#1 for(NSString *doll in dictionary[@"Nendoroid"]) { NSLog(@"%@", doll); } //#2 for(NSString *doll in dictionary[@"HarmoniaBloom"]) { NSLog(@"%@", doll); }
Trong đó:
- #1 thì sẽ không có gì được in ra và chương trình tiếp tục chạy. Chính là key không tồn tại, thì nhận về nil và chương trình tiếp tục chạy
- #2 thì có thể bị crash app (do không biết bây giờ Objective-C như thế nào nữa). Key tồn tại, thì nhận về một NSNull. Nó như các đối tượng khác và mong muốn có sự phản hồi lại. Nhưng điều này là không thể và chương trình sẽ bị crash.
Để hạn chế điều này, thì bạn chỉ cần kiểm tra nó với NSNull mà thôi. Khá đơn giản nhưng rất hiệu quả.
if(![dictionary[@"HarmoniaBloom"] isEqual:[NSNull null]]) { for(NSString *doll in dictionary[@"HarmoniaBloom"]) { NSLog(@"%@", doll); } }
Swift
Với tư thế là người kế tục Objective-C, nên Swift đã hạn chế đi rất nhiều lỗi từ quá khứ. Điển hình như các lỗi với Dictionary ở Objective-C thì Swift hạn chế ngay từ lúc khởi tạo. Cũng là ví dụ trên, ta sẽ sử dụng Swift để tạo đối tượng Dictionary nhóe. Xem ví dụ sau:
let jsonString = """ { "Pullip": [ "Classical Alice", "Eileen", "Alice Sepia" ], "Myou": [ "Delia", "Matcha" ], "HarmoniaBloom": null } """ let data = jsonString.data(using: .utf8)! let dictionary = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: [String]]
Chương trình sẽ crash ngay, để đảm bảo an toàn về mặt dữ kiệu là không có null hay nil. Còn khi bạn muốn sử dụng tiếp thì có thể lựa chọn Optional cho Dictionary của Swift.
let dictionary = try! JSONSerialization.jsonObject(with: data, options: []) as! [String: [String]?]
Chương trình lúc này sẽ oke hơn và khi bạn thực thi với các key như ví dụ trên thì sẽ có những kết quả khác nhau.
print(dictionary["Nendoroid"]) // nil print(dictionary["HarmoniaBloom"]) // Optional(nil)
Swift sẽ đủ sức phân biệt các giá trị nullabily trong các trường hợp khác nhau của Dictionary. NSNull có thể sử dụng được trong Swift, nhưng nó chỉ là cầu nối và không được dùng tới. Ngoài ra, với các Codable để parse JSON thì hỗ trợ bạn rất nhiều và bạn không cần quan tâm tới các kiểu nullability khi phân tích chúng. Tuy nhiên, khi bạn biết và phân biệt được chúng thì vấn tốt hơn.
Tạm kết
Nullability trong iOS thực sự là rất hỗn loạn cho dù là Swift hay Objective-C đi nữa. Nhưng khi bạn phân biệt được giữa nil & NSNull thì mọi thứ sẽ dễ dàng hơn rất nhiều. Quan trọng nhất bạn biết chúng sẽ đại điện cho điều gì, từ đó có những cách dùng phù hợp với chương trình của bạn.
Okay! Tới đây, mình xin kết thúc bài viết về nil & null . 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)