Nên dùng struct hay class trong Swift phần 1
Giới thiệu
Mấy lần trước mình có viết mấy bài như:
http://niviki.com/2017/04/quan-ly-bo-nho-trong-swift/
http://niviki.com/2017/03/toan-tap-protocols-trong-swift-3/
Thiệt tình mấy bài này dài quá, mỗi bài trên 1k5 từ. Nên lần này mình tách ra nhiều phần nhỏ viết cho mọi người dễ đọc, mình cũng có động lực viết hơn. Chứ viêt 1 bài dài cực lắm.
References Type và Values Type
Ở bài Quản lý bộ nhớ với Swift, mình có nói về kiểu references ( tham chiếu ) và value ( tham trị ) này rồi, trong bài đó có hình ảnh minh họa rõ ràng nữa, bạn nào chưa biết phần này thì xem qua nhé.
Apple cũng hướng Swift theo Value Oriented Programming (VoP), có nghĩa là đa số kiểu dữ liệu của Swift là value type như booleans, integers, strings, tuples, and enums. Cả array và dictionary cũng là value type luôn
var a = ["ni", "vi", "ki"] var b = a a.append(".com") print(b.count) // 3
Và một mảng kiểu reference type cũng như mảng value type luôn:
class Student { var name: String init(name: String) { self.name = name } }
let st1 = Student(name: "Khoa") let st2 = Student(name: "Alex") let st3 = Student(name: "My")
var students1 = [st1,st2] var students2 = students1
students1.append(st3) print(students2.count) // 2
Còn với reference type thì sẽ khác:
var st1 = Student(name: "Khoa") var st2 = Student(name: "Alex")
st2 = st1 st1.name = "Khoa Nguyen" print(st2.name) // Khoa Nguyen
Mình nhắc lại là đáng lẽ mình sẽ vẽ hình minh họa nhưng bài Quản lý bộ nhớ với Swift mình có vẽ cực kỳ chi tiết rồi, bạn có thể tham khảo thêm nhé.
Tóm lại, kiểu reference khi gán bằng thì sẽ chia sẻ địa chỉ của giá trị trong bộ nhớ với thằng được gán bằng, tức là sẽ có nhiều owners có quyền thay đổi giá trị đó. Như ví dụ trên khi gán st2 = st1 thì st1 đã chia sẻ địa chỉ của Student("Khoa Nguyen") cho thằng st2. Do st1 và st2 cùng là owners nên 1 thằng thay đổi thì cả 2 sẽ bị ảnh hưởng theo.
Ngược lại, kiểu value thì mỗi lần gán bằng giá trị sẽ được copy ra và mỗi thằng sẽ được 1 bản copy. Anh thay đổi thì kệ anh tôi không liên quan nữa
Tại sao dùng Struct?
Trong Swift, ngoài class thì closure cũng là reference type, nhưng để đa số mọi người sẽ phân vân giữa nên chọn struct hay class nhiều hơn vì closure nó thuộc thể loại khác rồi, mình sẽ viết về nó ở những bài sau nhé. Vì thế chúng ta sẽ đặt câu hỏi tại sao dùng struct, nó có ưu điểm gì?
Struct giảm đi mối quan hệ dây mơ rễ má trong code
Đầu tiên dễ thấy nhất là struct giảm đi mối quan hệ dây mơ rễ má trong code của bạn. Ví dụ bạn cần truyền param qua lại giữa các ViewController, nếu dùng class thì các ViewController đều là owner của các params này. Khi một ViewController thay đổi giá trị của params thì chúng sẽ bị ảnh hưởng ở toàn ứng dụng. Ví dụ dễ thấy nhất là khi truyền param khi xài segue hay Core Data.
Ví dụ bạn có 2 ViewController (VC): MainVC và ScoreVC.
Bạn cần truyền id từ MainVC qua các VC khác để dùng.
import UIKit class Student { var id: String init(id: String) { self.id = id } } class MainVC: UIViewController {
let khoa = Student(id: "1234") override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view, typically from a nib. }
override func viewWillAppear(\_ animated: Bool) {
super.viewWillAppear(animated)
print("viewWillAppear() MainVC ")
print("id: \\(khoa.id)")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "segue"{
if let secondVC = segue.destination as? ScoreVC {
if let st = sender as? Student{
secondVC.student = st
}
}
}
}
@IBAction func goBtn(\_ sender: Any) {
performSegue(withIdentifier: "segue", sender: khoa)
}
}
Và ở ScoreVC, bạn lỡ tay modify cái id này:
import UIKit
class ScoreVC: UIViewController {
var student: Student!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func back(\_ sender: Any) {
student.id = "00000"
dismiss(animated: true, completion: nil)
}
}
Thì console sẽ in ra như sau:
viewWillAppear() MainVC
id: 1234
viewWillAppear() MainVC
id: 00000
Id đã bị thay đổi, và nếu bạn tiếp tục pass cái id này sang các ViewController khác thì sẽ có bugs đúng không nào
Thread-safe
Thread-unsafe tức là có nhiều thread dùng chung một giá trị. struct hiển nhiên là Thread-safe rồi, vì mỗi thread sẽ có mỗi bản copy riêng, chúng đập lập với nhau, thread muốn làm gì làm cũng chả ảnh hưởng gì cả.
To be continued
Bài này mình đã nói về ưu điểm của Struct, vậy nó có khuyết điểm gì và class có ưu điểm gì hơn struct. Hẹn mọi người ở bài sau nhé. Mọi code/project demo mình đều up ở Github nhé: https://github.com/KhoaVanNguyen/Swift-Tutorials