- Bài 1: Tổng quan ngôn ngữ Swift
- Bài 2: Hướng dẫn cài đặt Xcode và chạy một playground
- Bài 3: Những cú pháp cơ bản trong swift
- Bài 4: Kiểu dữ liệu
- Bài 5: Toán tử trong ngôn ngữ lập trình Swift
- Bài 6: Câu lệnh rẽ nhánh trong swift – Decision Making
- Bài 7: Chuỗi ký tự trong Swift - Character và String
- Bài 8: Vòng lặp trong swift – Loops
- Bài 9: Mảng Array trong swift
- Bài 10: Dictionary trong swift
- Bài 11: Optional trong Swift
- Bài 12: Functions trong swift
- Bài 13: Closure trong swift
- Bài 14: Enumeration trong swift
- Bài 15: Struct trong swift
- Bài 16: Classes trong swift
- Bài 17: Properties trong swift
- Bài 18: Methods trong swift
- Bài 19: Subscripts trong swift
- Bài 20: Inheritance trong swift
- Bài 21: Initialization trong swift
- Bài 22: Deinitialization trong swift
- Bài 23: ARC trong swift
- Bài 24: Type casting trong swift
- Bài 25: Extensions trong swift
- Bài 26: Protocols trong swift
- Bài 27: Generics trong swift
- Bài 28: Access control trong swift
Bài 21: Initialization trong swift - Lập trình Swift cơ bản
Đăng bởi: Admin | Lượt xem: 2369 | Chuyên mục: Swift
Đối với một lập trình viên, trong một project bạn luôn phải liên tục khởi tạo các instance của các struct, class hoặc enum. Vì vậy, việc khởi tạo ngôn ngữ lập trình và hiểu rõ cũng như áp dụng thành thạo nó là một điều cực kỳ quan trọng. Điều này sẽ giúp bạn tăng performance cũng như chất lượng của source code.
Ở bài viết dưới đây, hãy cùng chúng tôi tìm hiểu kỹ hơn về quá trình khởi tạo trong ngôn ngữ lập trình Swift nhé.
Khởi tạo trong Structure
Khởi tạo mặc định
Khởi tạo mặc định cũng khá đơn giản và không gây quá nhiều khó khăn cho các lập trình viên.
let phoneX = Phone()
Syntax
init() {
// New Instance sẽ được khởi tạo ở đây
}
Example
struct Rectangle {
var length: Double
var breadth: Double
init() {
length = 12.0
breadth = 5.0
}
}
var rec = Rectangle()
print("area of rectangle is \(rec.length * rec.breadth)")
Ở đoạn code bên trên 1 instance là rec được khởi tạo với các thuộc tính là chiều rộng và chiều dài lần lượt là length = 12, và breadth = 5, sau khi chạy đoạn code trên sẽ thu được kết quả:
area of rectangle is 60.0
Khi xoá phần init() trong đoạn code trên ta sẽ được :
struct Rectangle {
var length: Double
var breadth: Double
}
var rec = Rectangle()
print("area of rectangle is \(rec.length * rec.breadth)")
Lỗi được thông báo khi chạy đoạn code là
error: missing argument for parameter 'length' in call
let rec = Rectangle()
Ở đây thì trình biên dịch đang bắt chúng ta phải khởi tạo với đối số của length nên đã thông báo lỗi.
Lý do là bạn cố khởi tạo cho struct Rectangle khi các stored property chưa được gán giá trị mặc định
Và ta có thể sửa đoạn code như sau:
struct Rectangle {
var length: Double = 12.0
var breadth: Double = 5.0
}
var rec = Rectangle()
print("area of rectangle is \(rec.length * rec.breadth)")
Chỉ cần gán giá trị mặc định cho tất cả các stored property bằng việc tạo ra instance rec với các stored property được gán giá trị ban đầu lần lượt là 12.0 và 5.0 và vấn đề sẽ được giải quyết ngay lập tức.
Như trên thì chúng ta đã được tiếp cận với việc khởi tạo 1 struct với kiểu mặc định, nhìn lại 2 cách viết ở trên nhé:
// 1
struct Rectangle {
var length: Double
var breadth: Double
init() {
length = 12.0
breadth = 5.0
}
}
// 2
struct Rectangle {
var length: Double = 12.0
var breadth: Double = 5.0
}
Với 2 cách viết nói trên sẽ đều đem đến cho chúng ta cùng một kết quả là 1 instance có các giá trị khởi tạo mặc định là 12.0 và 5.0. Bạn có thể áp dụng 1 trong 2 cách nhưng cách thứ 2 vẫn ưu việt hơn và dễ hiểu hơn.
Khởi tạo với các Parameter
struct Rectangle {
var length: Double
var breadth: Double
init(length: Double, breadth: Double) {
self.length = length
self.breadth = breadth
}
}
let rect = Rectangle(length: 6, breadth: 12)
print(" length is: \(rect.length)\n breadth is: \(rect.breadth) ")
Chúng ta sẽ nhận được kết quả như sau:
length is: 6.0
breadth is: 12.0
Ví dụ, nếu như phần phần init trong đoạn code trên bị xóa, điều gì sẽ xảy ra?
struct Rectangle {
var length: Double
var breadth: Double
}
let rect = Rectangle(length: 6, breadth: 12)
print(" length is: \(rect.length)\n breadth is: \(rect.breadth) ")
Và, đoạn code vẫn chạy một cách bình thường mà không bị báo lỗi. Điều này được lý giải rằng Swift đã tạo sẵn cho bạn 1 hàm khởi tạo rồi, tuy nhiên nếu bạn muốn đổi thứ tự các stored property thì khi gọi hàm khởi tạo mặc định thứ tự của Parameter cũng phải đổi tương ứng
struct Rectangle {
var breadth: Double
var length: Double
}
Khi gọi phải đảo lại parameter:
let rect = Rectangle(breadth: 6, length: 12)
- Khi thêm init với name các parameter khác với mặc định trong struct:
struct Rectangle {
var length: Double
var breadth: Double
init(length: Double) {
self.length = length
self.breadth = 5.0
}
}
let rect = Rectangle(length: 6, breadth: 12) // Lỗi
print(" length is: \(rect.length)\n breadth is: \(rect.breadth) ")
Lý do đoạn code trên bị lỗi là do chúng ta đã viết 1 hàm init với parameter là length ở trong struct dẫn tới swift không tự động viết hàm khởi tạo cho bạn nữa.
Vì vậy để vừa có hàm khởi tạo tự viết và vừa có hàm khởi tạo tự động mà swift tạo tự động, chúng ta cần chuyển hàm init của chúng ta qua extension:
struct Rectangle {
var length: Double
var breadth: Double
}
extension Rectangle {
init(length: Double) {
self.length = length
self.breadth = length + 5.0
}
}
Khởi tạo với các Parameter mặc định
struct Rectangle {
var length: Double
var breadth: Double
init(length: Double = 12.0, breadth: Double = 5.0) {
self.length = length
self.breadth = breadth
}
}
let rectA = Rectangle()
print(" rectA:\n length is: \(rectA.length)\n breadth is: \(rectA.breadth) ")
let rectB = Rectangle(length: 50)
print(" rectB:\n length is: \(rectB.length)\n breadth is: \(rectB.breadth) ")
let rectC = Rectangle(length: 20, breadth: 6)
print(" rectB:\n length is: \(rectC.length)\n breadth is: \(rectC.breadth) ")
Ta có kết quả là
rectA:
length is: 12.0
breadth is: 5.0
rectB:
length is: 50.0
breadth is: 5.0
rectB:
length is: 20.0
breadth is: 6.0
Việc khởi tạo với các parameter giúp chúng ta gọi hàm đa dạng hơn
Initializer Delegation
Initializer Delegation thực chất chính là việc gọi hàm khởi tạo từ các hàm khởi tạo khác.
Bạn có thể xem ví dụ dưới đây để hiểu rõ hơn nhé
struct Rectangle {
var length: Double
var breadth: Double
// 1
init(length: Double, breadth: Double) {
self.length = length
self.breadth = breadth
}
// 2
init(length: Double) {
let breadth = length + 2.0
self.init(length: length, breadth: breadth)
}
}
let rect = Rectangle(length: 50)
print(" rect:\n length is: \(rect.length)\n breadth is: \(rect.breadth) ")
Và chúng ta nhận được kết quả như sau:
rect:
length is: 50.0
breadth is: 52.0
Với đoạn code trên chúng ta có tới 2 hàm init trong struct, ở hàm init thứ 2 thì chúng ta thực hiện tính toán bề rộng trước khi gọi đến hàm init 1. Và đây được gọi là Initializer Delegation
Nếu như bạn muốn gọi self trước khi call hàm init số 1. Bạn cần thực hiện như sau
struct Rectangle {
var length: Double
var breadth: Double
// 1
init(length: Double, breadth: Double) {
self.length = length
self.breadth = breadth
}
// 2
init(length: Double) {
self.length = 5.0 // Báo lỗi: 'self' used before 'self.init' call or assignment to 'self'
let breadth = length + 2.0
self.init(length: length, breadth: breadth)
}
}
let rect = Rectangle(length: 50)
print(" rect:\n length is: \(rect.length)\n breadth is: \(rect.breadth) ")
Lúc này thì compiler báo lỗi: ‘self’ used before ‘self.init’ call or assignment to ‘self’
Lỗi này cho biết chúng ta không được dùng self trước khi gọi Initializer Delegation. Giả sử như đoạn code trên pass qua compiler thì lúc này length = 50.0 chứ không phải bằng 5.0 như mong muốn.
Chú ý trong phần I:
- Khi khởi tạo 1 instance, chúng ta cần gán giá trị ban đầu cho tất cả các non-optional stored property.
- Không gọi self trước khi call Initializer Delegation
Khởi tạo trong Class
Hai loại khởi tạo trong class
1.1. Designated Initializer
Đây có lẽ được coi là khởi tạo chính của class và để hàm được khởi tạo, bạn cần gán giá trị ban đầu cho tất cả các non-optional stored property.
class Rectangle {
var length: Double
var breadth: Double
// Designated Initializer
init(length: Double, breadth: Double) {
self.length = length
self.breadth = breadth
}
}
Khi class của bạn có stored property là non-optional thì bắt buộc phải khởi tạo với Designated Initializer.
1.2. Convenience Initializers
Convenience Initializers là hàm hỗ trợ việc khởi tạo của class, trong hàm khởi tạo này chúng ta sẽ gọi đến các hàm khởi tạo khác
class Rectangle {
var length: Double
var breadth: Double
// Designated Initializer
init(length: Double, breadth: Double) {
self.length = length
self.breadth = breadth
}
// Convenience Initializer
convenience init(length: Double, area: Double) {
let breadth = area / length
self.init(length: length, breadth: breadth)
}
}
let rect = Rectangle(length: 5, area: 20)
print(" rect:\n length is: \(rect.length)\n breadth is: \(rect.breadth) ")
Chúng ta sẽ nhận được kết quả dưới đây:
rect:
length is: 5.0
breadth is: 4.0
Khởi tạo với subclass
class Rectangle {
var length: Double
var breadth: Double
// Designated Initializer
init(length: Double, breadth: Double) {
self.length = length
self.breadth = breadth
}
}
class Square: Rectangle {
var area: Double
}
Khi chạy đoạn code trên, màn hình sẽ báo lỗi
stored property 'area' without initial value prevents synthesized initializers
var area: Double
Như đã lưu ý ở phần trên, khi khởi tạo 1 instance bất kỳ thì luôn phải đảm bảo các giá trị mặc định cho tất cả các stored property non-optional và ở đây do đã thêm biến area ở subclass là Square nên swift yêu cầu phải gán giá trị.
Dưới đây là 2 cách để pass qua compiler
// option 1
class Square: Rectangle {
var color: String = "red"
}
// option 2
class Square: Rectangle {
var color: String
init(length: Double, breadth: Double, color: String) {
self.color = color
super.init(length: length, breadth: breadth)
}
}
Nếu viết như sau, chúng ta sẽ nhận được
class Square: Rectangle {
var color: String
init(length: Double, breadth: Double, color: String) {
self.color = color
self.length = length
self.breadth = breadth
}
}
Nhận được error với nội dung:
error: 'self' used in property access 'length' before 'super.init' call
self.length = length
Compiler thông báo rằng bạn đang sử dụng self để truy cập vào property length trước khi gọi super.init. (super ở đây chính là class cha Rectangle) và việc dùng super.init là bắt buộc để gán giá trị cho length and breadth.
Thừa kế hàm khởi tạo
class Square: Rectangle {
var color: String
init(length: Double, breadth: Double, color: String) {
self.color = color
super.init(length: length, breadth: breadth)
}
override init(length: Double, breadth: Double) {
self.color = "red"
super.init(length: length, breadth: breadth)
}
}
Chỉ với một vài dòng code đơn giản, thao tác nhanh gọn là bạn hoàn toàn có thể sử dụng hàm khởi tạo của class cha một cách ngon lành rồi.
Theo dõi VnCoder trên Facebook, để cập nhật những bài viết, tin tức và khoá học mới nhất!
- Bài 1: Tổng quan ngôn ngữ Swift
- Bài 2: Hướng dẫn cài đặt Xcode và chạy một playground
- Bài 3: Những cú pháp cơ bản trong swift
- Bài 4: Kiểu dữ liệu
- Bài 5: Toán tử trong ngôn ngữ lập trình Swift
- Bài 6: Câu lệnh rẽ nhánh trong swift – Decision Making
- Bài 7: Chuỗi ký tự trong Swift - Character và String
- Bài 8: Vòng lặp trong swift – Loops
- Bài 9: Mảng Array trong swift
- Bài 10: Dictionary trong swift
- Bài 11: Optional trong Swift
- Bài 12: Functions trong swift
- Bài 13: Closure trong swift
- Bài 14: Enumeration trong swift
- Bài 15: Struct trong swift
- Bài 16: Classes trong swift
- Bài 17: Properties trong swift
- Bài 18: Methods trong swift
- Bài 19: Subscripts trong swift
- Bài 20: Inheritance trong swift
- Bài 21: Initialization trong swift
- Bài 22: Deinitialization trong swift
- Bài 23: ARC trong swift
- Bài 24: Type casting trong swift
- Bài 25: Extensions trong swift
- Bài 26: Protocols trong swift
- Bài 27: Generics trong swift
- Bài 28: Access control trong swift