Tại sao Swift khởi tạo các lớp con thích hợp trước tiên?


9

Trong ngôn ngữ Swift, để khởi tạo một thể hiện, người ta phải điền vào tất cả các trường của lớp đó và chỉ sau đó gọi siêu cấu trúc:

class Base {
    var name: String

    init(name: String) {
        self.name = name
    }
}

class Derived: Base {
    var number: Int

    init(name: String, number: Int) {
        // won't compile if interchange lines
        self.number = number
        super.init(name)
    }
}

Đối với tôi nó có vẻ ngược, bởi vì cá thể cần selfđược tạo trước khi gán giá trị cho các trường của nó và mã đó tạo ấn tượng như thể chuỗi chỉ xảy ra sau khi gán. Ngoài ra, siêu lớp không có phương tiện hợp pháp để đọc các thuộc tính được giới thiệu của lớp con của nó, vì vậy tính an toàn không được tính trong trường hợp này.

Ngoài ra, nhiều ngôn ngữ khác, như JavaScript và thậm chí cả Objective C, vốn là tổ tiên tinh thần của Swift, yêu cầu gọi chuỗi trước khi truy cập self, không phải sau đó.

Lý do đằng sau sự lựa chọn này để yêu cầu các trường được xác định trước khi gọi siêu cấu trúc là gì?


Hấp dẫn. Có một hạn chế nào mà các yêu cầu phương thức (cụ thể là ảo) có thể được đặt, chẳng hạn như, chỉ nói, sau khi xâu chuỗi? Trong C #, các trường của lớp con được cung cấp giá trị mặc định, sau đó xây dựng chuỗi / siêu lớp, sau đó bạn có thể khởi tạo chúng cho thực, IIRC ..
Erik Eidt

3
Vấn đề là bạn cần khởi tạo tất cả các trường trước khi cho phép truy cập không hạn chế self.
CodeInChaos

@ErikEidt: Trong Swift, chỉ các trường tùy chọn được tự động khởi tạo thành không.
gnasher729

Câu trả lời:


9

Trong C ++, khi bạn tạo một đối tượng Derogen, nó bắt đầu như một đối tượng Base trong khi hàm tạo Base đang chạy, vì vậy tại thời điểm hàm tạo Base chạy, các thành viên Derogen thậm chí không tồn tại. Vì vậy, chúng không cần phải được khởi tạo và chúng không thể được khởi tạo. Chỉ khi hàm tạo cơ sở kết thúc, đối tượng được thay đổi thành đối tượng Derogen với rất nhiều trường chưa được khởi tạo mà sau đó bạn khởi tạo.

Trong Swift, khi bạn tạo một đối tượng Derogen, nó là một đối tượng Derogen ngay từ đầu. Nếu các phương thức bị ghi đè, thì phương thức Base init sẽ sử dụng các phương thức được ghi đè, có thể truy cập các biến thành viên Derogen. Do đó, tất cả các biến thành viên Derogen phải được khởi tạo trước khi phương thức Base init được gọi.

Tái bút Bạn đã đề cập đến Mục tiêu-C. Trong Objective-C, mọi thứ được tự động khởi tạo thành 0 / nil / NO. Nhưng nếu giá trị đó không phải là giá trị chính xác để khởi tạo một biến, thì phương thức Base init có thể dễ dàng gọi một phương thức bị ghi đè và sử dụng biến chưa được khởi tạo với giá trị 0 thay vì giá trị đúng. Trong Mục tiêu-C, đó không phải là vi phạm quy tắc ngôn ngữ (đó là cách nó được xác định để hoạt động) nhưng rõ ràng là một lỗi trong mã của bạn. Trong Swift, lỗi đó không được ngôn ngữ cho phép.

Tái bút Có một nhận xét "nó là một đối tượng xuất phát từ đầu, hoặc nó không thể quan sát được do các quy tắc ngôn ngữ"? Lớp Derogen đã khởi tạo các thành viên của chính nó trước khi phương thức Base init được gọi và các thành viên Derogen này giữ các giá trị của chúng. Vì vậy, nó một đối tượng Derogen tại thời điểm Base init được gọi, hoặc trình biên dịch sẽ phải làm một cái gì đó khá kỳ quái. Và ngay sau khi phương thức Base init đã khởi tạo tất cả các thành viên thể hiện Base, nó có thể gọi các hàm bị ghi đè và điều đó sẽ chứng minh nó là một thể hiện của lớp dẫn xuất.


Rất hợp lý. Tôi lấy tự do để thêm một ví dụ. Vui lòng quay lại nếu tôi không rõ ràng hoặc hiểu sai quan điểm.
Zomagk

Đây có phải là một đối tượng Xuất phát từ đầu, hoặc các quy tắc ngôn ngữ làm cho điều đó không thể quan sát được? Tôi nghĩ đó là cái sau.
Ded repeatator

9

Điều này xuất phát từ các quy tắc an toàn của Swift, như được giải thích trong phần Khởi tạo hai pha trên trang Khởi tạo của tài liệu ngôn ngữ .

Nó đảm bảo rằng mọi trường được đặt trước khi sử dụng (đặc biệt là con trỏ, để tránh sự cố).

Swift đạt được điều này với trình tự khởi tạo hai pha: Mỗi trình khởi tạo phải khởi tạo tất cả các trường đối tượng của nó, sau đó gọi trình khởi tạo siêu lớp để thực hiện tương tự, và chỉ sau khi điều đó xảy ra, các trình khởi tạo này mới được phép selfthoát con trỏ, ví dụ cuộc gọi phương thức, hoặc đọc các giá trị của các thuộc tính thể hiện.

Sau đó, họ có thể thực hiện khởi tạo thêm, đảm bảo rằng đối tượng được hình thành tốt. Đặc biệt, tất cả các con trỏ không tùy chọn sẽ có giá trị hợp lệ. nil không hợp lệ cho họ.

Mục tiêu C không khác nhau nhiều, ngoại trừ 0 hoặc nil luôn là một giá trị hợp lệ, do đó, việc khởi tạo pha đầu tiên được thực hiện bởi người cấp phát chỉ đặt tất cả các trường thành 0. Ngoài ra, Swift có các trường không thay đổi, vì vậy chúng phải được khởi tạo trong giai đoạn một . Và Swift thi hành các quy tắc an toàn này.


Tất nhiên, sẽ khó hơn nhiều với MI.
Ded repeatator

3

Xem xét

  • Một phương thức ảo được định nghĩa trong lớp cơ sở có thể được định nghĩa lại trong lớp dẫn xuất.
  • Nhà thầu của lớp cơ sở có thể gọi phương thức ảo này trực tiếp hoặc gián tiếp.
  • Các định nghĩa lại phương thức ảo (trong lớp có nguồn gốc) có thể phụ thuộc vào giá trị của một lĩnh vực trong lớp là bộ có nguồn gốc chính xác trong thầu lớp dẫn xuất.
  • Nhà thầu của lớp dẫn xuất có thể gọi một phương thức trong lớp cơ sở phụ thuộc vào các trường đã được đặt trong nhà thầu lớp cơ sở.

Do đó , không có thiết kế đơn giản nào giúp nhà thầu an toàn khi cho phép các phương thức ảo , Swift tránh các vấn đề này bằng cách yêu cầu Khởi tạo hai pha, do đó mang lại sự an toàn tốt hơn cho lập trình viên, đồng thời dẫn đến ngôn ngữ phức tạp hơn.

Nếu bạn có thể giải quyết những vấn đề này một cách tốt đẹp, đừng vượt qua "Đi", hãy tiếp tục trực tiếp đến bộ sưu tập PHd của bạn

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.