Swift - Khởi tạo

Các lớp, cấu trúc và kiểu liệt kê đã từng được khai báo trong Swift 4 được khởi tạo để chuẩn bị thể hiện của một lớp. Giá trị ban đầu được khởi tạo cho thuộc tính được lưu trữ và cũng cho các phiên bản mới, các giá trị cũng được khởi tạo để tiếp tục. Từ khóa tạo hàm khởi tạo được thực hiện bằng phương thức 'init ()'. Bộ khởi tạo Swift 4 khác với Objective-C là nó không trả về bất kỳ giá trị nào. Chức năng của nó là kiểm tra việc khởi tạo các thể hiện mới được tạo trước khi xử lý. Swift 4 cũng cung cấp quy trình 'deinitialization' để thực hiện các hoạt động quản lý bộ nhớ khi các cá thể được phân bổ.

Vai trò của bộ khởi tạo cho các thuộc tính được lưu trữ

Thuộc tính lưu trữ phải khởi tạo các cá thể cho các lớp và cấu trúc của nó trước khi xử lý các cá thể đó. Các thuộc tính được lưu trữ sử dụng bộ khởi tạo để gán và khởi tạo các giá trị do đó loại bỏ nhu cầu gọi các trình quan sát thuộc tính. Bộ khởi tạo được sử dụng trong tài sản được lưu trữ

  • Để tạo giá trị ban đầu.

  • Để gán giá trị thuộc tính mặc định trong định nghĩa thuộc tính.

  • Để khởi tạo một thể hiện cho một kiểu dữ liệu cụ thể 'init ()' được sử dụng. Không có đối số nào được truyền vào bên trong hàm init ().

Cú pháp

init() {
   //New Instance initialization goes here
}

Thí dụ

struct rectangle {
   var length: Double
   var breadth: Double
   init() {
      length = 6
      breadth = 12
   }
}

var area = rectangle()
print("area of rectangle is \(area.length*area.breadth)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

area of rectangle is 72.0

Ở đây cấu trúc 'hình chữ nhật' được khởi tạo với chiều dài và chiều rộng thành viên là kiểu dữ liệu 'Đôi'. Phương thức Init () được sử dụng để khởi tạo các giá trị cho chiều dài thành viên mới được tạo và nhân đôi. Diện tích hình chữ nhật được tính và trả về bằng cách gọi hàm hình chữ nhật.

Đặt giá trị thuộc tính theo mặc định

Ngôn ngữ Swift 4 cung cấp hàm Init () để khởi tạo các giá trị thuộc tính được lưu trữ. Ngoài ra, người dùng có quyền khởi tạo các giá trị thuộc tính theo mặc định trong khi khai báo các thành viên lớp hoặc cấu trúc. Khi thuộc tính nhận cùng một giá trị trong suốt chương trình, chúng ta có thể khai báo nó trong phần khai báo một mình thay vì khởi tạo nó trong init (). Đặt giá trị thuộc tính theo mặc định cho phép người dùng khi kế thừa được xác định cho các lớp hoặc cấu trúc.

struct rectangle {
   var length = 6
   var breadth = 12
}

var area = rectangle()
print("area of rectangle is \(area.length*area.breadth)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

area of rectangle is 72

Ở đây thay vì khai báo độ dài và độ rộng trong init (), các giá trị được khởi tạo trong chính khai báo.

Khởi tạo tham số

Trong ngôn ngữ Swift 4, người dùng có điều kiện khởi tạo các tham số như một phần trong định nghĩa của trình khởi tạo bằng cách sử dụng init ().

struct Rectangle {
   var length: Double
   var breadth: Double
   var area: Double
   
   init(fromLength length: Double, fromBreadth breadth: Double) {
      self.length = length
      self.breadth = breadth
      area = length * breadth
   }
   init(fromLeng leng: Double, fromBread bread: Double) {
      self.length = leng
      self.breadth = bread
      area = leng * bread
   }
}

let ar = Rectangle(fromLength: 6, fromBreadth: 12)
print("area is: \(ar.area)")

let are = Rectangle(fromLeng: 36, fromBread: 12)
print("area is: \(are.area)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

area is: 72.0
area is: 432.0

Tham số cục bộ & bên ngoài

Tham số khởi tạo có cả tên tham số cục bộ và toàn cục tương tự như tên tham số hàm và phương thức. Khai báo tham số cục bộ được sử dụng để truy cập bên trong thân khởi tạo và khai báo tham số bên ngoài được sử dụng để gọi bộ khởi tạo. Bộ khởi tạo Swift 4 khác với bộ khởi tạo chức năng và phương thức là chúng không xác định bộ khởi tạo nào được sử dụng để gọi các hàm nào.

Để khắc phục điều này, Swift 4 giới thiệu một tên bên ngoài tự động cho mỗi và mọi tham số trong init (). Tên bên ngoài tự động này tương đương với tên cục bộ được viết trước mọi tham số khởi tạo.

struct Days {
   let sunday, monday, tuesday: Int
   init(sunday: Int, monday: Int, tuesday: Int) {
      self.sunday = sunday
      self.monday = monday
      self.tuesday = tuesday
   }
   init(daysofaweek: Int) {
      sunday = daysofaweek
      monday = daysofaweek
      tuesday = daysofaweek
   }
}

let week = Days(sunday: 1, monday: 2, tuesday: 3)
print("Days of a Week is: \(week.sunday)")
print("Days of a Week is: \(week.monday)")
print("Days of a Week is: \(week.tuesday)")

let weekdays = Days(daysofaweek: 4)
print("Days of a Week is: \(weekdays.sunday)")
print("Days of a Week is: \(weekdays.monday)")
print("Days of a Week is: \(weekdays.tuesday)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

Days of a Week is: 1
Days of a Week is: 2
Days of a Week is: 3
Days of a Week is: 4
Days of a Week is: 4
Days of a Week is: 4

Tham số không có tên bên ngoài

Khi tên bên ngoài không cần thiết cho dấu gạch dưới khởi tạo '_' được sử dụng để ghi đè hành vi mặc định.

struct Rectangle {
   var length: Double
   
   init(frombreadth breadth: Double) {
      length = breadth * 10
   }
   init(frombre bre: Double) {
      length = bre * 30
   }
   init(_ area: Double) {
      length = area
   }
}

let rectarea = Rectangle(180.0)
print("area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

area is: 180.0
area is: 370.0
area is: 110.0

Các loại tài sản tùy chọn

Khi thuộc tính được lưu trữ tại một số trường hợp không trả về bất kỳ giá trị nào thì thuộc tính được khai báo với kiểu 'tùy chọn' cho biết rằng 'không có giá trị' nào được trả về cho kiểu cụ thể đó. Khi thuộc tính được lưu trữ được khai báo là 'tùy chọn', nó sẽ tự động khởi tạo giá trị thành 'nil' trong quá trình khởi tạo chính nó.

struct Rectangle {
   var length: Double?
   
   init(frombreadth breadth: Double) {
      length = breadth * 10
   }
   init(frombre bre: Double) {
      length = bre * 30
   }
   init(_ area: Double) {
      length = area
   }
}

let rectarea = Rectangle(180.0)
print("area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

area is: Optional(180.0)
area is: Optional(370.0)
area is: Optional(110.0)

Sửa đổi các thuộc tính không đổi trong quá trình khởi tạo

Khởi tạo cũng cho phép người dùng sửa đổi giá trị của thuộc tính hằng số. Trong quá trình khởi tạo, thuộc tính lớp cho phép các thể hiện lớp của nó được sửa đổi bởi lớp siêu chứ không phải lớp con. Hãy xem xét ví dụ trong chương trình trước, 'độ dài' được khai báo là 'biến' trong lớp chính. Biến chương trình dưới đây 'length' được sửa đổi thành biến 'hằng số'.

struct Rectangle {
   let length: Double?
   
   init(frombreadth breadth: Double) {
      length = breadth * 10
   }
   init(frombre bre: Double) {
      length = bre * 30
   }
   init(_ area: Double) {
      length = area
   }
}

let rectarea = Rectangle(180.0)
print("area is: \(rectarea.length)")

let rearea = Rectangle(370.0)
print("area is: \(rearea.length)")

let recarea = Rectangle(110.0)
print("area is: \(recarea.length)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

area is: Optional(180.0)
area is: Optional(370.0)
area is: Optional(110.0)

Trình khởi tạo mặc định

Trình khởi tạo mặc định cung cấp một thể hiện mới cho tất cả các thuộc tính đã khai báo của lớp cơ sở hoặc cấu trúc với các giá trị mặc định.

class defaultexample {
   var studname: String?
   var stmark = 98
   var pass = true
}
var result = defaultexample()

print("result is: \(result.studname)")
print("result is: \(result.stmark)")
print("result is: \(result.pass)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau. -

result is: nil
result is: 98
result is: true

Chương trình trên được định nghĩa với tên lớp là 'defaultexample'. Ba hàm thành viên được khởi tạo theo mặc định là 'studname?' để lưu trữ các giá trị 'nil', 'stmark' là 98 và 'pass' là giá trị Boolean 'true'. Tương tự như vậy, các giá trị thành viên trong lớp có thể được khởi tạo mặc định trước khi xử lý các kiểu thành viên của lớp.

Thành viên khởi tạo cho các loại cấu trúc

Khi người dùng không cung cấp các trình khởi tạo tùy chỉnh, các loại Cấu trúc trong Swift 4 sẽ tự động nhận 'trình khởi tạo thành viên'. Chức năng chính của nó là khởi tạo các cá thể cấu trúc mới với khởi tạo thành viên mặc định và sau đó các thuộc tính của cá thể mới được chuyển cho khởi tạo thành viên theo tên.

struct Rectangle {
   var length = 100.0, breadth = 200.0
}
let area = Rectangle(length: 24.0, breadth: 32.0)

print("Area of rectangle is: \(area.length)")
print("Area of rectangle is: \(area.breadth)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

Area of rectangle is: 24.0
Area of rectangle is: 32.0

Các cấu trúc được khởi tạo theo mặc định cho các hàm thành viên của chúng trong quá trình khởi tạo cho 'chiều dài' là '100.0' và 'chiều rộng' là '200.0'. Nhưng các giá trị bị ghi đè trong quá trình xử lý các biến chiều dài và chiều rộng là 24.0 và 32.0.

Ủy quyền trình khởi tạo cho các loại giá trị

Ủy quyền khởi tạo được định nghĩa là gọi các trình khởi tạo từ các trình khởi tạo khác. Chức năng chính của nó là hoạt động như khả năng tái sử dụng để tránh trùng lặp mã trên nhiều bộ khởi tạo.

struct Stmark {
   var mark1 = 0.0, mark2 = 0.0
}
struct stdb {
   var m1 = 0.0, m2 = 0.0
}

struct block {
   var average = stdb()
   var result = Stmark()
   init() {}
   init(average: stdb, result: Stmark) {
      self.average = average
      self.result = result
   }

   init(avg: stdb, result: Stmark) {
      let tot = avg.m1 - (result.mark1 / 2)
      let tot1 = avg.m2 - (result.mark2 / 2)
      self.init(average: stdb(m1: tot, m2: tot1), result: result)
   }
}

let set1 = block()
print("student result is: \(set1.average.m1, set1.average.m2)
\(set1.result.mark1, set1.result.mark2)")

let set2 = block(average: stdb(m1: 2.0, m2: 2.0),
result: Stmark(mark1: 5.0, mark2: 5.0))
print("student result is: \(set2.average.m1, set2.average.m2)
\(set2.result.mark1, set2.result.mark2)")

let set3 = block(avg: stdb(m1: 4.0, m2: 4.0),
result: Stmark(mark1: 3.0, mark2: 3.0))
print("student result is: \(set3.average.m1, set3.average.m2)
\(set3.result.mark1, set3.result.mark2)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

(0.0,0.0) (0.0,0.0)
(2.0,2.0) 5.0,5.0)
(2.5,2.5) (3.0,3.0)

Quy tắc ủy quyền trình khởi tạo

Các loại giá trị Các loại lớp
Kế thừa không được hỗ trợ cho các kiểu giá trị như cấu trúc và kiểu liệt kê. Tham khảo các trình khởi tạo khác được thực hiện thông qua self.init Kế thừa được hỗ trợ. Kiểm tra tất cả các giá trị thuộc tính được lưu trữ được khởi tạo

Kế thừa và Khởi tạo Lớp

Các loại lớp có hai loại trình khởi tạo để kiểm tra xem các thuộc tính được lưu trữ đã xác định có nhận được giá trị ban đầu hay không, cụ thể là trình khởi tạo được chỉ định và trình khởi tạo tiện lợi.

Bộ khởi tạo được chỉ định và Bộ khởi tạo tiện lợi

Bộ khởi tạo được chỉ định Trình khởi tạo Tiện lợi
Được coi là khởi tạo chính cho một lớp Được coi là hỗ trợ khởi tạo cho một lớp
Tất cả các thuộc tính của lớp đều được khởi tạo và bộ khởi tạo siêu lớp thích hợp được gọi để khởi tạo thêm Bộ khởi tạo được chỉ định được gọi với bộ khởi tạo tiện lợi để tạo cá thể lớp cho một trường hợp sử dụng cụ thể hoặc kiểu giá trị đầu vào
Ít nhất một bộ khởi tạo được chỉ định được xác định cho mọi lớp Không cần phải xác định bắt buộc các trình khởi tạo tiện lợi khi lớp không yêu cầu trình khởi tạo.
Init (tham số) {câu lệnh} tiện ích init (tham số) {câu lệnh}

Chương trình cho Bộ khởi tạo được Chỉ định

class mainClass {
   var no1 : Int // local storage
   init(no1 : Int) {
      self.no1 = no1 // initialization
   }
}

class subClass : mainClass {
   var no2 : Int // new subclass storage
   init(no1 : Int, no2 : Int) {
      self.no2 = no2 // initialization
      super.init(no1:no1) // redirect to superclass
   }
}

let res = mainClass(no1: 10)
let print = subClass(no1: 10, no2: 20)

print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

res is: 10
res is: 10
res is: 20

Chương trình khởi tạo tiện lợi

class mainClass {
   var no1 : Int // local storage
   init(no1 : Int) {
      self.no1 = no1 // initialization
   }
}

class subClass : mainClass {
   var no2 : Int
   init(no1 : Int, no2 : Int) {
      self.no2 = no2
      super.init(no1:no1)
   }
   // Requires only one parameter for convenient method
   override convenience init(no1: Int) {
      self.init(no1:no1, no2:0)
   }
}

let res = mainClass(no1: 20)
let print = subClass(no1: 30, no2: 50)

print("res is: \(res.no1)")
print("res is: \(print.no1)")
print("res is: \(print.no2)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

res is: 20
res is: 30
res is: 50

Kế thừa và Ghi đè khởi tạo

Swift 4 không cho phép các lớp con của nó kế thừa các bộ khởi tạo siêu lớp của nó cho các kiểu thành viên của chúng theo mặc định. Kế thừa chỉ áp dụng cho các bộ khởi tạo siêu hạng ở một mức độ nào đó sẽ được thảo luận trong Kế thừa bộ khởi tạo tự động.

Khi người dùng cần có các trình khởi tạo được định nghĩa trong siêu lớp, thì lớp con với các trình khởi tạo phải được người dùng định nghĩa là triển khai tùy chỉnh. Khi việc ghi đè phải được thực hiện bởi lớp con thì từ khóa 'ghi đè' của lớp siêu phải được khai báo.

class sides {
   var corners = 4
   var description: String {
      return "\(corners) sides"
   }
}

let rectangle = sides()
print("Rectangle: \(rectangle.description)")

class pentagon: sides {
   override init() {
      super.init()
      corners = 5
   }
}

let bicycle = pentagon()
print("Pentagon: \(bicycle.description)")

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

Rectangle: 4 sides
Pentagon: 5 sides

Công cụ khởi tạo được chỉ định và thuận tiện trong hành động

class Planet {
   var name: String
   init(name: String) {
      self.name = name
   }
   convenience init() {
      self.init(name: "[No Planets]")
   }
}

let plName = Planet(name: "Mercury")
print("Planet name is: \(plName.name)")

let noplName = Planet()
print("No Planets like that: \(noplName.name)")

class planets: Planet {
   var count: Int
   init(name: String, count: Int) {
      self.count = count
      super.init(name: name)
   }
   override convenience init(name: String) {
      self.init(name: name, count: 1)
   }
}

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

Planet name is: Mercury
No Planets like that: [No Planets]

Bộ khởi tạo khả dụng

Người dùng phải được thông báo khi có bất kỳ lỗi khởi tạo nào trong khi xác định một lớp, cấu trúc hoặc các giá trị liệt kê. Việc khởi tạo các biến đôi khi trở thành một lỗi do

  • Giá trị tham số không hợp lệ.
  • Thiếu nguồn bên ngoài được yêu cầu.
  • Điều kiện ngăn quá trình khởi tạo thành công.

Để bắt các ngoại lệ do phương thức khởi tạo ném ra, Swift 4 tạo ra một khởi tạo linh hoạt được gọi là 'bộ khởi tạo khả dụng' để thông báo cho người dùng rằng có điều gì đó không được chú ý khi khởi tạo cấu trúc, lớp hoặc thành viên liệt kê. Từ khóa để bắt trình khởi tạo có sẵn là 'init?'. Ngoài ra, không thể xác định các bộ khởi tạo khả dụng và không khả dụng với các loại tham số và tên giống nhau.

struct studrecord {
   let stname: String
   init?(stname: String) {
      if stname.isEmpty {return nil }
      self.stname = stname
   }
}
let stmark = studrecord(stname: "Swing")

if let name = stmark {
   print("Student name is specified")
}
let blankname = studrecord(stname: "")

if blankname == nil {
   print("Student name is left blank")
}

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

Student name is specified
Student name is left blank

Các công cụ khởi tạo có sẵn cho các bảng kê

Ngôn ngữ Swift 4 cung cấp sự linh hoạt để có các bộ khởi tạo sẵn có cho các bảng liệt kê để thông báo cho người dùng khi các thành viên của bảng liệt kê không còn khởi tạo các giá trị.

enum functions {
   case a, b, c, d
   init?(funct: String) {
      switch funct {
      case "one":
         self = .a
      case "two":
         self = .b
      case "three":
         self = .c
      case "four":
         self = .d
      default:
         return nil
      }
   }
}
let result = functions(funct: "two")

if result != nil {
   print("With In Block Two")
}
let badresult = functions(funct: "five")

if badresult == nil {
   print("Block Does Not Exist")
}

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

With In Block Two
Block Does Not Exist

Trình khởi tạo khả dụng cho các lớp

Một bộ khởi tạo khả dụng khi được khai báo với các kiểu liệt kê và cấu trúc sẽ cảnh báo lỗi khởi tạo trong bất kỳ trường hợp nào trong quá trình triển khai nó. Tuy nhiên, bộ khởi tạo khả dụng trong các lớp sẽ chỉ cảnh báo lỗi sau khi các thuộc tính được lưu trữ đã được đặt thành giá trị ban đầu.

class studrecord {
   let studname: String!
   init?(studname: String) {
      self.studname = studname
      if studname.isEmpty { return nil }
   }
}

if let stname = studrecord(studname: "Failable Initializers") {
   print("Module is \(stname.studname)")
}

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

Module is Optional("Failable Initializers")

Ghi đè một trình khởi tạo khả dụng

Tương tự như khởi tạo, người dùng cũng có điều kiện ghi đè bộ khởi tạo sẵn có của lớp cha bên trong lớp con. Khởi tạo khả dụng siêu lớp cũng có thể được ghi đè bằng trong một trình khởi tạo không khả dụng của lớp con.

Bộ khởi tạo lớp con không thể ủy quyền cho bộ khởi tạo lớp con khi ghi đè một bộ khởi tạo lớp siêu khả dụng bằng cách khởi tạo lớp con không khả dụng.

Bộ khởi tạo không khả dụng không bao giờ có thể ủy quyền cho bộ khởi tạo khả dụng.

Chương trình đưa ra bên dưới mô tả các bộ khởi tạo khả dụng và không khả dụng.

class Planet {
   var name: String
   
   init(name: String) {
      self.name = name
   }
   convenience init() {
      self.init(name: "[No Planets]")
   }
}
let plName = Planet(name: "Mercury")
print("Planet name is: \(plName.name)")

let noplName = Planet()
print("No Planets like that: \(noplName.name)")
   
class planets: Planet {
   var count: Int
   
   init(name: String, count: Int) {
      self.count = count
      super.init(name: name)
   }
   override convenience init(name: String) {
      self.init(name: name, count: 1)
   }
}

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

Planet name is: Mercury
No Planets like that: [No Planets]

Init! Bộ khởi tạo khả dụng

Swift 4 cung cấp 'init?' để xác định một bộ khởi tạo sẵn có phiên bản tùy chọn. Để xác định một phiên bản tùy chọn không được bao bọc hoàn toàn của kiểu cụ thể 'init!' được quy định.

struct studrecord {
let stname: String

   init!(stname: String) {
      if stname.isEmpty {return nil }
      self.stname = stname
   }
}
let stmark = studrecord(stname: "Swing")

if let name = stmark {
   print("Student name is specified")
}

let blankname = studrecord(stname: "")

if blankname == nil {
   print("Student name is left blank")
}

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

Student name is specified
Student name is left blank

Bộ khởi tạo bắt buộc

Để khai báo mỗi và mọi lớp con của từ khóa khởi tạo 'bắt buộc' cần phải được xác định trước hàm init ().

class classA {
   required init() {
      var a = 10
      print(a)
   }
}

class classB: classA {
   required init() {
      var b = 30
      print(b)
   }
}

let res = classA()
let print = classB()

Khi chúng tôi chạy chương trình trên bằng sân chơi, chúng tôi nhận được kết quả sau:

10
30
10