Chính xác thì init coder aDecoder là gì?


122

Tôi đang học cách phát triển iOS từ một khóa học trực tuyến và mỗi khi tôi tạo chế độ xem tùy chỉnh (ô xem bảng tùy chỉnh, ô xem bộ sưu tập, v.v.), người hướng dẫn luôn triển khai trình khởi tạo này:

required init?(coder aDecoder: NSCoder) {
    super.init(coder: aDecoder)
}

Chính xác tại sao tôi luôn phải gọi điều này? Nó làm gì? Tôi có thể đặt thuộc tính bên trong init không?


5
Câu trả lời này sẽ giúp bạn stackoverflow.com/questions/24036393/... Cảm ơn bạn
Seungyoun Yi

2
Nếu bạn phân lớp một đối tượng thực hiện NSCodingthì bạn cần triển khai bộ khởi tạo này, vì nó là yêu cầu của các lớp thực hiện NSCoding. Ít nhất bạn phải gọi phương thức init siêu lớp. Nếu NSCoderchứa các thuộc tính được mã hóa cho lớp của bạn thì bạn có thể sử dụng phương pháp này để khôi phục các thuộc tính đó
Paulw11

1
Ngoài ra, tôi khuyên bạn nên đọc phần về việc nhập đối tượng trong cuốn sách Swift chính thức của Apple.
Nicolas Miari

Câu trả lời:


121

Tôi sẽ bắt đầu câu trả lời này từ hướng ngược lại: nếu bạn muốn lưu trạng thái của chế độ xem của mình vào đĩa thì sao? Đây được gọi là tuần tự hóa . Ngược lại là deserialization - khôi phục trạng thái của đối tượng từ đĩa.

Các NSCodinggiao thức định nghĩa hai phương pháp để serialize và deserialize đối tượng:

encodeWithCoder(_ aCoder: NSCoder) {
    // Serialize your object here
}

init(coder aDecoder: NSCoder) {
    // Deserialize your object here
}

Vậy tại sao nó lại cần trong lớp tùy chỉnh của bạn? Câu trả lời là Interface Builder. Khi bạn kéo một đối tượng lên bảng phân cảnh và định cấu hình nó, Interface Builder sẽ tuần tự hóa trạng thái của đối tượng đó vào đĩa, sau đó giải mã hóa nó khi bảng phân cảnh xuất hiện trên màn hình. Bạn cần nói với Interface Builder cách thực hiện những điều đó. Ít nhất, nếu bạn không thêm bất kỳ thuộc tính mới nào vào lớp con của mình, bạn có thể chỉ cần yêu cầu lớp cha đóng gói và giải nén cho bạn, do đó sẽ super.init(coder: aDecoder)gọi. Nếu lớp con của bạn phức tạp hơn, bạn cần thêm mã tuần tự hóa và giải mã hóa của riêng mình cho lớp con.

Điều này trái ngược với cách tiếp cận của Visual Studio, đó là viết mã vào một tệp ẩn để tạo đối tượng tại thời điểm chạy.


Tại sao không đặt mọi thứ vào bên trong AwakeFromNib và quên sử dụng init(coder aCoder : NSCoder)?
Honey,

@Honey - trong một từ, "đôi khi bạn không thể làm điều đó". Bạn thường có thể nhưng không phải lúc nào cũng vậy.
Fattie,

@Fattie là các chi tiết không làm nó quá phức tạp hoặc không cần thiết để biết? Nếu không bạn có phiền giải thích không?
Honey

9
@Honey nếu bạn muốn định cấu hình đối tượng của mình trong Trình tạo giao diện thì awakeFromNibsẽ không hoạt động. awakeFromNibđược gọi vào thời gian chạy . Bất cứ điều gì bạn làm trong Trình tạo giao diện đều trong thời gian thiết kế . Để thực hiện những gì bạn đã làm trong thời gian thiết kế để chạy thời gian được encodeWithCoder(tiết kiệm) và init(coder:)(tải)
Mã khác nhau

3
@Honey nếu bạn không sử dụng giao diện Builder để cấu hình lớp tùy chỉnh của bạn (tức là làm điều đó theo chương trình với mã) sau đó bạn có thể làm điều đó trong awakeFromNibhoặcinitWIthFrame
Mã khác nhau

28

Yêu cầu triển khai trình khởi tạo đó là hệ quả của hai điều:

  1. Các Liskov nguyên tắc thay thế . Nếu S là một lớp con của T (ví dụ MyViewControllerlà một lớp con của ViewController), thì các đối tượng S (trường hợp của MyViewController) phải có thể được thay thế ở nơi mà đối tượng T (trường hợp của ViewController) được mong đợi.

  2. Trình khởi tạo không được kế thừa trong Swift nếu bất kỳ trình khởi tạo nào được định nghĩa rõ ràng trong lớp con. Nếu một trình khởi tạo được cung cấp rõ ràng, thì tất cả các trình khởi tạo khác phải được cung cấp rõ ràng (sau đó có thể gọi super.init(...)). Xem câu hỏi này để biết cơ sở lý luận. Nó bằng Java, nhưng vẫn được áp dụng.

Đến điểm 1, mọi thứ mà bản gốc ViewControllercó thể làm, MyViewControllerlớp con sẽ có thể làm được. Một trong những điều đó là có thể được khởi tạo từ một cho trước NSCoder. Đến điểm 2, MyViewControllerlớp con của bạn không tự động kế thừa khả năng này. Do đó, bạn phải cung cấp thủ công trình khởi tạo đáp ứng yêu cầu này. Trong trường hợp này, bạn chỉ cần ủy quyền cho lớp cha, để nó thực hiện những gì nó thường làm.


1
Nó có ý nghĩa hoàn hảo rằng các hàm tạo không được kế thừa: Nếu bạn khởi tạo một thể hiện của lớp dẫn xuất bằng cách sử dụng bộ khởi tạo (được kế thừa) của lớp cơ sở, các thuộc tính không được kế thừa mới được định nghĩa ("thêm") bởi lớp dẫn xuất sẽ không bao giờ được khởi tạo.
Nicolas Miari

3
Trên thực tế, trình khởi tạo được kế thừa trong Swift, do bạn không cung cấp bất kỳ triển khai trình khởi tạo nào của riêng bạn trong lớp con của bạn. Nếu các thuộc tính không kế thừa mới được xác định của bạn có các giá trị mặc định, bạn có thể không cần viết bất kỳ bộ khởi tạo nào trong lớp con của mình và chỉ cần kế thừa tất cả các bộ khởi tạo của lớp cha của bạn. Xem ở đây
TheBaj
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.