Contents
Chào bạn đến với Fx Studio. Bài viết này sẽ trình bày một phần rất hẹp trong ngôn ngữ Swift. Đó là Convenience Initializer. Để chuẩn bị vào bài, bạn cần phải nắm được một số kiến thức cơ bản như sau:
Chuẩn bị
- Swift 5.x
- Playground
Để demo code cho bài này, chỉ cần sử dụng Playground là ổn rồi. Bạn tự sáng tạo thêm các ví dụ cho riêng bạn, hoặc dùng chính nhưng class mà bạn đang gặp vấn đề để giải quyết nó luôn.
1. Convenience Initializer là gì?
Bạn là một iOS Developer thì có bao giờ bạn tự hỏi là:
Có bao nhiều loại cho hàm khởi tạo của
class
trong ngôn ngữ lập trình Swift?
Mình đảm bảo là sẽ trên 80% dev được hỏi, thì sẽ không biết câu trả lời. Vì một điều hiển nhiên là Swift đã cung cấp cho bạn các Default Initializers
sẵn rồi.
Ví dụ sau:
class ShoppingListItem { var name: String? var quantity = 1 var purchased = false } var item = ShoppingListItem()
Như bạn đã thấy, khi các thuộc tính của class đã được cung cấp giá trị lúc khai báo. Thì cái hàm init
mặc định đó sẽ không cần dùng tới. Và nó sẽ được gọi khi thực thi lệnh ShoppingListitem()
.
Vậy
Mấy cái hàm
init
viết thêm thì gọi là gì?
Vâng, chúng nó sẽ được gọi là Designated initializers
. Cái thứ mà hiển nhiên tồn tại lâu ni cũng có cái tên nghe sang chảnh phải không. Dành cho bạn nào quên cú pháp thì xem hình dưới (hình chuẩn từ Apple).
Và, nhiều lúc viết thêm thuộc tính hay viết thêm hàm init
thì Xcode báo lỗi. Và theo một quy luật hiển nhiên nữa, dev iOS lại cứ nhấn fix auto một cách vô thức. Lúc đó:
Từ khoá
convenience
sẽ xuất hiện và bạn có thắc mắc nó là gì không?
(nếu bạn để ý tới nói, chứ thật ra là cũng trên 80% dev iOS cũng không để ý tới)
Convenience Initializer là một hàm khởi tạo phụ trợ cho các hàm khởi tạo khác trong class. Sử dụng để bổ sung các giá trị cho các thuộc tính hoặc tuỳ biến lại một số thuộc tính với các giá trị đầu vào khác nhau cho từng trường hợp.
Cú pháp như sau:
Ví dụ với code cho nó thông não một tý nha:
class BaseClass { var a: Int var b: Int init(a: Int, b: Int) { self.a = a self.b = b } convenience init(numbers: [Int]) { self.init(a: numbers[0], b: numbers[1]) } func show() { print("a = \(a), b = \(b)") } }
Theo trên thì có 1 class là BaseClass
với 2 thuộc tính là a
và b
. Và có 1 hàm khởi tạo với 2 tham số cho a & b.
Viết thêm một hàm khởi tạo phụ trợ với tham số là Array Int
. Nhưng chúng ta không thay đổi gì hết, chỉ cần lấy ra 2 số và gọi hàm init
kia mà thôi. Đơn giản phải không nào, còn cách dùng như sau:
let obj1 = BaseClass(a: 1, b: 2) obj1.show() let obj2 = BaseClass(numbers: [5,10]) obj2.show()
2. Các trường hợp sử dụng Convenience Initializer
Tóm tắt một chút:
Designated initializers
là các hàm khởi tạo truyền thống như trước đây.Convenience Initializer
là các hàm khởi tạo phụ trợ thêm cho các hàm truyền thống.
Và một điều chú ý:
Cũng có nhưng quy định riêng cho Convenience Initializer trong khi sử dụng.
Ta xem qua hình sau là mô tả thiết kế cho 2 loại hàm khởi tạo trong class
của ngôn ngữ Swift.
Đợi một ngày đẹp trời nào đó, mình sẽ viết một bài nghiêm chỉnh giải thích tường tận sơ đồ trên của Swift. Và giải thích các luật áp dụng cho 2 loại hàm khởi tạo với kiểu Type Class trong Swift.
Việc quan trọng là dựa vào sơ đồ trên mà mình tóm tắt ra 3 cách dùng cơ bản nhất cho convenience
.
2.1. Phụ trợ
Đây là cách sử dụng chính của Convenience Initializer. Cho dù là Supper Class
hay SubClass
đi nữa, hoặc bất kỳ class nào … Thì bạn cần phải thành thạo việc sử dụng này.
Tập trung vào việc phụ trợ thêm cho các hàm khởi tạo khác có sẵn trong class. Đó là về mặt cung cấp giá trị cho các thuộc tính được khai báo hoặc tính toán dữ liệu lúc khởi tạo.
Ví dụ, bạn có 1 class là Human
như sau:
class Human { // Properties var name: String // init init(name: String) { self.name = name } // methods func show() { print("Name: \(name)") } }
Và khi sử dụng cho việc tạo đối tượng thì như thế này:
let boo = Human(name: "Boo") boo.show()
Nhưng bạn muốn khởi tạo 1 đối tượng với lệnh đơn giản là Human()
. Thì điều này là không thể được, vì 2 nguyên nhân cơ bản:
- Bạn chưa viết lại hàm
init()
- Bạn cần phải cung cấp giá trị cho biến
name
Giải quyết việc này thì nhờ tới convenience init
và đám code còn lại không đụng tới.
convenience init() { self.init(name: "[NoName]") }
Và việc gọi hàm Human()
với không có tham số name
, đối tượng Human vẫn được tạo ra, nhưng name
sẽ là [NoName]
.
2.2. Kế thừa
Mục đích sử dụng tiếp theo sử dụng trong Kế thừa với lớp con. Bài toán rất cụ thể, lớp cha (Supper Class) đã chịu khó khởi tạo với rất nhiều thuộc tính của nó. Và lớp con (Subclass) có thêm vài thuộc tính của riêng nó nữa, nhưng bạn không cần thiết phải khởi tạo hết tất cả thuộc tính ở 2 class đó.
Ta có bài toán được minh hoạ như sau:
- BaseClass
class BaseClass { var a: Int var b: Int init(a: Int, b: Int) { self.a = a self.b = b } }
- SubClass
class SubClass : BaseClass { var c: Int var d: Int }
Và đây là hàm khởi tạo mà SubClass phải có. Nó khởi tạo bao gồm 2 phần:
- Phần 1: sẽ gán các giá trị cho các thuộc tính của riêng nó
- Phần 2: gọi tới
super
để gán các giá trị còn lại
init(a: Int, b: Int, c: Int, d: Int) { self.c = c self.d = d super.init(a: a, b: b) }
Giờ đây, bạn muốn custom 1 hàm init
với chỉ 2 giá trị c & d. Còn a & b sẽ bằng 0. Với convenience
:
convenience init(c: Int, d: Int) { self.c = c self.d = d super.init(a: 0, b: 0) }
Và Xcode sẽ báo lỗi. Vì do, convenience
sẽ không truy cập được tới các hàm của lớp cha.
Cho nên bạn cần phải gọi như sau:
convenience init(c: Int, d: Int) { self.init(a: 0, b: 0, c: c, d: d) }
2.3. Ghi đè
Hoặc bạn sẽ có thêm một cách toàn mỹ hơn như sau:
class SubClass : BaseClass { var c: Int var d: Int override init(a: Int, b: Int) { c = 0 d = 0 super.init(a: a, b: b) } convenience init(a: Int, b: Int, c: Int, d: Int) { self.init(a: a, b: b) self.c = c self.d = d } }
Thay vì viết một hàm init
mới với 4 tham số. Thì bạn chỉ cần override
lại hàm khởi tạo của lớp cha. Truyền các giá trị mặc định cho các thuộc tính của lớp con.
Cuối cùng, viết lại một hàm convenience
đầy đủ 4 tham số, nhưng thao tác ngược lại với cách (2).
- Phần 1: khởi tạo trước
- Phần 2: gán giá trị sau
Với cách này, để cho bạn thêm cách xử lý khi nào cho thuộc tính của lớp nào với các giá trị mặc định. Hoặc khi nào cần phụ trợ riêng cho thuộc tính của lớp nào.
Qua 3 cách sử dụng cơ bản trên, hi vọng giúp ích bạn trong một số trường hợp khó khăn. Hoặc chủ động hơn, bớt lệ thuộc vào trình biên dịch hay gỡ lỗi của Xcode.
Tạm kết
- Biết thêm về 2 loại hình khởi tạo class
- Cách sử dụng Convenience Initializer
- Cách hoạt động của các hàm Initializer trong Subclass
Bài viết được tham khảo tại đây! Cảm ơn bạn đã đọc bài viết này!
And Happy coding!
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)