Khởi tạo và kế thừa một Class

Nhiệm vụ của một bộ khởi tạo

Class là bản thiết kế.

Object là một đối tượng, và là một thể hiện của 1 class.

Khởi tạo là quá trình chuẩn bị trước khi sử dụng của một thể hiện của một class, struct, hoặc enum. Để triển khai một quá trình khởi tạo, người ta phải định nghĩa các bộ khởi tạo. Trong quá trình khởi tạo, tất cả các stored property của một class (bao gồm cả những stored property của nó và nó được kế thừa) phải được gán một giá trị đầu tiên (giá trị khởi tạo).

Để minh hoạ cho điều này chúng ta xem xét một ví dụ: một máy tính bắt buộc phải có ram, chip, main, HDD. Nó cũng có thể có thêm chuột máy tính đi theo máy (mouse)

Screen Shot 2016-01-26 at 8.23.24 PM.png

Một máy tính suất xưởng, nó phải được ráp main, chip, ram, và hdd xác định. Một máy tính không thể có một cái main nào đó được! Nhưng nó cũng có thể có một con chuột theo máy hoặc không. Quá trình khởi tạo giống như quá trình lắp ráp một chiếc máy, nó đảm bảo chắc chắn việc những thành phần phải có của class được khởi tạo và không rơi vào tình trạng bất định.

Swift có hai loại bộ khởi tạo cho class là bộ khởi tạo theo thiết kế (designated initializers), và bộ khởi tạo tiện lợi (convenience initializers)

Designated Initializers VS Convenience Initializers

Như tên gọi của các bộ khởi tạo, bộ khởi tạo thiết kế là bộ khởi tạo chính, bộ khởi tạo cơ bản.

Nhưng trong một số trường hợp, để tạo sự thuận lợi cho việc khởi tạo, chúng ta truyền vào những tham số mặc định cho bộ khởi tạo cơ bản.

Quay lại ví dụ.

Máy tính DellVostro cũng là một loại máy tính, nó kế thừa thiết kế của máy tính. Vì vậy nó cũng có ram, chip, main, hdd, và mouse. Và nó cũng có một bộ khởi tạo được kế thừa lại từ lớp Computer.

Screen Shot 2016-01-26 at 8.47.05 PM.png

Dell Vostro là dòng máy tính có cấu hình gần giống nhau. Vì vậy để thuận tiện cho việc khởi tạo, ta tạo ra một hàm convenience init() với những giá trị mặc định được khởi tạo sẵn.

Thay vì việc phải truyền từng ram, chip, main, hdd vào thì ta chỉ việc gọi init() để tạo ra những máy tính Dell Vostro với cấu hình cơ bản.

Hai giai đoạn khởi tạo

Giai đoạn 1: Gán các giá trị khởi tạo cho các stored property. Giai đoạn này xảy ra trước khi uỷ quyền khởi tạo cho bộ khởi tạo của superClass.

** Giai đoạn 2 😗* Giai đoạn là cơ hội cuối để chỉnh sửa các property trước khi một instance của class sẵn sàng để sử dụng.

Chúng ta thay đổi ví dụ trên một chút:

Screen Shot 2016-01-26 at 9.20.56 PM.png

Trong class Computer chúng ta thay đổi một chút khi gán ngay giá trị mặc định cho hdd.

Trong class DellVostro chúng ta cố gắng gán lại giá trị cho hdd. Nhưng không thành công.

Như vậy, rõ ràng là Giai đoạn 1 của khởi tạo, chỉ dành cho những stored property chưa được khởi tạo giá. Những stored property đã được khởi tạo mà chúng ta muốn thay đổi giá trị thì cần phải đợi đến giai đoạn 2 của quá trình khởi tạo.

Cũng tương tự như vậy, chúng ta cũng không thể gọi 1 method trong giai đoạn 1, vì 1 method là một loại type đã luôn được khởi tạo cùng class

Một số quy tắc uỷ quyền khởi tạo

  1. Bộ khởi tạo Cơ bản chỉ được gọi lên bộ khởi tạo Cơ bản của superClass

  2. Bộ khởi tạo tiện dụng chỉ được gọi đến bộ khởi tạo cơ bản trong lớp đó.

Diễn giải 2 quy tắc này chúng ta sẽ có:

Nếu có 1 bộ khởi tạo mà gọi đến super.init(...) thì chắc chắn bộ khởi tạo này là Cơ bản

Nếu có 1 bộ khởi tạo mà gọi dến self.init(...) thì chắc chắn bộ khởi tạo này là bộ khởi tạo tiện dụng.

Bộ khởi tạo Cơ bản thì chỉ gọi đến bộ khởi tạo Cơ bản ở Superclass mà không gọi đến các bộ khởi tạo trong class được.

2 quy tắc Tự động kế thừa

  1. Nếu subclass không định nghĩa bất cứ bộ khởi tạo cơ bản nào thì nó tự động được kế thừa toàn bộ các bộ khởi tạo cơ bản của lớp cha.
  2. Nếu subclass thoả mãn điều kiện 1 (tức là ko có bất cứ bộ khởi tạo cơ bản nào) hoặc customize lại toàn bộ các bộ khởi tạo cơ bản của lớp cha thì nó sẽ được kế thừa toàn bộ các bộ khởi tạo tiện dụng của lớp cha.

screen-shot-2016-01-21-at-2-59-50-pm.png

Trong trường hợp này. Do UIView có 2 bộ khởi tạo cơ bản, mà subClass lại chỉ implement 1 bộ khởi tạo init(coder aDecoder: NSCoder). Vì vậy subClass không được kế thừa bộ khởi tạo convenience init()

screen-shot-2016-01-21-at-3-04-45-pm.png

Ở trường hợp này, SubClass đã implement đầy đủ cả 2 bộ khởi tạo cơ bản, vì vậy nó được kế thừa đầy đủ các bộ khởi tạo convenience của UIView trong đó có convenience init()

Để tham khảo thêm, bạn có thể xem thêm nội dung này trong blog của tôi: https://thangtm7.wordpress.com/2016/01/21/class-inheritance-and-initialization/

All Rights Reserved