Khi làm việc trong Swift có khi nào bạn tự hỏi khi nào thì sử dụng Struct, khi nào thì sử dụng Classes không ?
bài viết trên medium:
https://medium.com/better-programming/struct-vs-classes-in-swift-the-differences-explained-1e164a22efa6
Cannot assign to property: function call returns immutable value.
Bạn có lẽ không phải là người đầu tiên quay lại sử dụng class khi gặp phải lỗi trên. Một structs dường như quá khó để làm việc cùng. Hy vọng sau khi đọc xong bài viết này bạn sẽ làm việc với structs dễ dàng hơn.
Class trong Swift là gì?
Một class là một kiểu tham chiếu (reference type) trong Swift. Nó có thể bao gồm:
. properties
. methods
. subscripts
. initializers
. protocol conformances
. extensions
Nó thường được miêu tả như một định nghĩa mẫu cho một đối tượng, như khởi tạo Article sau đây:
class ArticleClass {
let title: String
let url: URL
var readCount: Int = 0
init(title: String, url: URL) {
self.title = title
self.url = url
}
}
Struct trong Swift là gì?
Một struct trong Swift là một kiểu tham trị (value type), giống như class nó cũng bao gồm:
. properties
. methods
. subscripts
. initializers
. protocol conformances
. extensions
Nó cũng có thể tạo một định nghĩa cho một đối tượng, như sau:
struct ArticleStruct {
let title: String
let url: URL
var readCount: Int = 0
}
Sự khác nhau giữa Class và Struct :
Trong vị dụ trên xem ra Struct và Class hầu hết là giống nhau. Đó chính là vấn đề giữa chúng, nhìn chúng tương tự nhau. Tuy vậy có khá nhiều sự khác nhau quan trọng mà chúng ta phải nhận thức được
Kiểu tham chiếu và tham trị (value vs reference types)
Sự khác biệt quan trọng nhất là struct là tham trị trong khi class là tham chiếu. Sẽ có một bài viết riêng cho vấn đề này, nhưng một giải thích ngắn cũng đủ hiểu được vấn đề:
Tham chiếu tới một khởi tạo class sẽ chia sẻ dữ liệu, có nghĩa là một vài thay đổi trong class đó sẽ làm thay đổi trên các tham chiếu khác. Như trong ví dụ dưới đây:
Một struct là một tham trị và sẽ tạo ra một copy duy nhất cho mỗi tham chiếu mới. Bạn có thể thấy sự khác nhau quan trọng như là readCount sẽ chỉ được thay đổi trong tham chiếu khởi tạo:
Lợi ích việc đột biến có lợi
Với điều này, struct là đột biến an toàn, bạn có thể tin rằng sẽ không có phần nào khác trong ứng dụng của bạn thay đổi dữ liệu cùng lúc. Điều này làm cho việc sự dụng code có lợi ích đặc biệt trong môi trường multi-threaded nơi mà những thread khác nhau có thể thay đổi dữ liệu cùng lúc. Điều này có thể tạo lên những lỗi khó chịu và rất khó để debug.
Không có đột biến này thì structs và classes sẽ hoàn toàn giống nhau.
Structs và Constants
Một loại tham trị khác được sử dụng là Constants. Nếu bạn lanh lợi, bạn có thể thấy articleStruct đã được định nghĩa như là một biến thay thế let giống như chúng ta làm với articleClass. Nếu bạn sử dụng let để khai báo một struct, sẽ xảy ra lỗi như sau:
Một struct chỉ có thể được thay đổi nếu nó được định nghĩa như là một biến và nó chỉ update trong tham chiếu đầu tiên.
Structs có một hàm khởi tạo sẵn có
Nếu bạn quay lại và so sánh 2 đoạn code trên, bạn sẽ thấy lớp ArticleClass có một hàm khởi tạo, cái được yêu cầu cho class. Structs sẽ có sẵn một hàm khởi tạo của nó.
Điều này trở lên tốt hơn với SE-242, cái đã được implemented trong Swift 5.1 và thêm thành phần khởi tạo cho struct. Điều này có nghĩa như sau:
Class cho phép kế thừa
Class có thể kế thừa từ class khác và với điều đó, nó hoạt động như một class abstract. Một ví dụ phổ biến là một custom view controller loại kế thừa từ UIViewController.
Nhưng với protocols trong Swift, kế thừa trở lên không cần thiết nữa, nó có thể thay thế với protocols. Protocols có thể sử dụng với cả hai classes và structs, trong khi kế thừa chỉ có thể sử dụng trong class mà thôi.
Class có thể huỷ bỏ
Một class cho phép xử lý huỷ nó bằng cách sử dụng phương thức method deinit(). Khi bạn định nghĩa một phương thức tương tự deinit trong struct bạn sẽ gặp một thông báo lỗi như sau:
| Deinitializer may only be declared within a class
Vậy khi nào nên sử dụng class và khi nào thì nên sử dụng struct?
Trong document Swift đã miêu tả như sau:
Những khả năng bổ xung cái mà classes hỗ trợ sẽ làm tăng thêm chi phí bộ nhớ. Như một cách tổng quát, structures sẽ tốt hơn và chỉ sử dụng classes khi chúng ta cảm thấy thích hợp hoặc cần thiết. Trong thực tế, điều này có nghĩa là hầu hết các trường hợp loại custom data mà bạn định nghĩa sẽ là structures và enumerations.
Điều này giải thích hầu hết các topics chúng ta tham khảo ở trên. Vì vậy, khi làm việc cùng Cocoa classes, bạn sẽ thường yêu cầu subclass từ NSObject cái mà yêu cầu bạn sử dụng một class.
Một danh sách gạch đầu dòng sẽ làm cho quyết định dễ dàng hơn.
Bạn nên sử dụng class khi mà:
_ So sánh hàm khởi tạo là cần thiết sử dụng ===
_ Chia sẻ dữ liệu thay đổi được yêu cầu
_ Khả năng tương tác Object-C được yêu cầu
Bạn nển sử dụng struct khi mà:
_ So sánh hàm khởi tạo là cần thiết sử dụng ==
_ Bản copy là duy nhất với một trạng thái độc lập được yêu cầu
_ Dữ liệu được sử dụng trong multiple threads
Bất kì lời khuyên vàng bạn cần tới ?
Yes, tôi có! Có gắng sử dụng struct như là mặc định. Struct sẽ làm cho code của bạn trở lên dễ dàng hơn cả khi làm việc trong môi trường đa luồng, cái mà chúng ta thường sử dụng khi phát triển Swift.
Nếu bạn quyết định sử dụng class, hãy xem xét làm nó như là final và giúp đỡ trình duyệt bằng cách bảo nó là không có classes nào khác kế thừa từ class mà bạn định nghĩa.
Tổng kết
Hy vọng rằng bạn đã có thể chọn lựa để sử dụng giữa class và struct. Sử dụng Struct không phải lúc nào cũng dễ dàng và có thể sử dụng, nhưng nó nên là một mặc định trong trương chình của bạn. Khi sử dụng struct thường xuyên hơn, bạn sẽ dần cảm thấy dễ dàng.
bài viết trên medium:
https://medium.com/better-programming/struct-vs-classes-in-swift-the-differences-explained-1e164a22efa6
Cannot assign to property: function call returns immutable value.
Bạn có lẽ không phải là người đầu tiên quay lại sử dụng class khi gặp phải lỗi trên. Một structs dường như quá khó để làm việc cùng. Hy vọng sau khi đọc xong bài viết này bạn sẽ làm việc với structs dễ dàng hơn.
Class trong Swift là gì?
Một class là một kiểu tham chiếu (reference type) trong Swift. Nó có thể bao gồm:
. properties
. methods
. subscripts
. initializers
. protocol conformances
. extensions
Nó thường được miêu tả như một định nghĩa mẫu cho một đối tượng, như khởi tạo Article sau đây:
class ArticleClass {
let title: String
let url: URL
var readCount: Int = 0
init(title: String, url: URL) {
self.title = title
self.url = url
}
}
Struct trong Swift là gì?
Một struct trong Swift là một kiểu tham trị (value type), giống như class nó cũng bao gồm:
. properties
. methods
. subscripts
. initializers
. protocol conformances
. extensions
Nó cũng có thể tạo một định nghĩa cho một đối tượng, như sau:
struct ArticleStruct {
let title: String
let url: URL
var readCount: Int = 0
}
Sự khác nhau giữa Class và Struct :
Trong vị dụ trên xem ra Struct và Class hầu hết là giống nhau. Đó chính là vấn đề giữa chúng, nhìn chúng tương tự nhau. Tuy vậy có khá nhiều sự khác nhau quan trọng mà chúng ta phải nhận thức được
Kiểu tham chiếu và tham trị (value vs reference types)
Sự khác biệt quan trọng nhất là struct là tham trị trong khi class là tham chiếu. Sẽ có một bài viết riêng cho vấn đề này, nhưng một giải thích ngắn cũng đủ hiểu được vấn đề:
Tham chiếu tới một khởi tạo class sẽ chia sẻ dữ liệu, có nghĩa là một vài thay đổi trong class đó sẽ làm thay đổi trên các tham chiếu khác. Như trong ví dụ dưới đây:
let articleClass = ArticleClass(title: "Struct vs Class", url: URL(string: "www.avanderlee.com")!)
let articleClassCopy = articleClass
articleClass.readCount = 10
print(articleClassCopy.readCount) // Prints: 10
Một struct là một tham trị và sẽ tạo ra một copy duy nhất cho mỗi tham chiếu mới. Bạn có thể thấy sự khác nhau quan trọng như là readCount sẽ chỉ được thay đổi trong tham chiếu khởi tạo:
var articleStruct = ArticleStruct(title: "Struct vs Class", url: URL(string: "www.avanderlee.com")!, readCount: 0) var articleStructCopy = articleStruct This could create nasty bugs which are hard to debug. articleStruct.readCount = 10 print(articleStructCopy.readCount) // Prints: 0
Lợi ích việc đột biến có lợi
Với điều này, struct là đột biến an toàn, bạn có thể tin rằng sẽ không có phần nào khác trong ứng dụng của bạn thay đổi dữ liệu cùng lúc. Điều này làm cho việc sự dụng code có lợi ích đặc biệt trong môi trường multi-threaded nơi mà những thread khác nhau có thể thay đổi dữ liệu cùng lúc. Điều này có thể tạo lên những lỗi khó chịu và rất khó để debug.
Không có đột biến này thì structs và classes sẽ hoàn toàn giống nhau.
Structs và Constants
Một loại tham trị khác được sử dụng là Constants. Nếu bạn lanh lợi, bạn có thể thấy articleStruct đã được định nghĩa như là một biến thay thế let giống như chúng ta làm với articleClass. Nếu bạn sử dụng let để khai báo một struct, sẽ xảy ra lỗi như sau:
Một struct chỉ có thể được thay đổi nếu nó được định nghĩa như là một biến và nó chỉ update trong tham chiếu đầu tiên.
Structs có một hàm khởi tạo sẵn có
Nếu bạn quay lại và so sánh 2 đoạn code trên, bạn sẽ thấy lớp ArticleClass có một hàm khởi tạo, cái được yêu cầu cho class. Structs sẽ có sẵn một hàm khởi tạo của nó.
Điều này trở lên tốt hơn với SE-242, cái đã được implemented trong Swift 5.1 và thêm thành phần khởi tạo cho struct. Điều này có nghĩa như sau:
// Before Swift 5.1 Memberwise initializers:
// Generated memberwise init: init(title: String, url: URL, readCount: Int)
let article = ArticleStruct(title: "", url: URL(string: "")!, readCount: 0)
// After Swift 5.1 Memberwise initializers, using the default 0 for read count
// Generated memberwise init: init(title: String, url: URL, readCount: Int = 0)
let article = ArticleStruct(title: "", url: URL(string: "")!)
Class cho phép kế thừa
Class có thể kế thừa từ class khác và với điều đó, nó hoạt động như một class abstract. Một ví dụ phổ biến là một custom view controller loại kế thừa từ UIViewController.
Nhưng với protocols trong Swift, kế thừa trở lên không cần thiết nữa, nó có thể thay thế với protocols. Protocols có thể sử dụng với cả hai classes và structs, trong khi kế thừa chỉ có thể sử dụng trong class mà thôi.
Class có thể huỷ bỏ
Một class cho phép xử lý huỷ nó bằng cách sử dụng phương thức method deinit(). Khi bạn định nghĩa một phương thức tương tự deinit trong struct bạn sẽ gặp một thông báo lỗi như sau:
| Deinitializer may only be declared within a class
Vậy khi nào nên sử dụng class và khi nào thì nên sử dụng struct?
Trong document Swift đã miêu tả như sau:
Những khả năng bổ xung cái mà classes hỗ trợ sẽ làm tăng thêm chi phí bộ nhớ. Như một cách tổng quát, structures sẽ tốt hơn và chỉ sử dụng classes khi chúng ta cảm thấy thích hợp hoặc cần thiết. Trong thực tế, điều này có nghĩa là hầu hết các trường hợp loại custom data mà bạn định nghĩa sẽ là structures và enumerations.
Điều này giải thích hầu hết các topics chúng ta tham khảo ở trên. Vì vậy, khi làm việc cùng Cocoa classes, bạn sẽ thường yêu cầu subclass từ NSObject cái mà yêu cầu bạn sử dụng một class.
Một danh sách gạch đầu dòng sẽ làm cho quyết định dễ dàng hơn.
Bạn nên sử dụng class khi mà:
_ So sánh hàm khởi tạo là cần thiết sử dụng ===
_ Chia sẻ dữ liệu thay đổi được yêu cầu
_ Khả năng tương tác Object-C được yêu cầu
Bạn nển sử dụng struct khi mà:
_ So sánh hàm khởi tạo là cần thiết sử dụng ==
_ Bản copy là duy nhất với một trạng thái độc lập được yêu cầu
_ Dữ liệu được sử dụng trong multiple threads
Bất kì lời khuyên vàng bạn cần tới ?
Yes, tôi có! Có gắng sử dụng struct như là mặc định. Struct sẽ làm cho code của bạn trở lên dễ dàng hơn cả khi làm việc trong môi trường đa luồng, cái mà chúng ta thường sử dụng khi phát triển Swift.
Nếu bạn quyết định sử dụng class, hãy xem xét làm nó như là final và giúp đỡ trình duyệt bằng cách bảo nó là không có classes nào khác kế thừa từ class mà bạn định nghĩa.
Tổng kết
Hy vọng rằng bạn đã có thể chọn lựa để sử dụng giữa class và struct. Sử dụng Struct không phải lúc nào cũng dễ dàng và có thể sử dụng, nhưng nó nên là một mặc định trong trương chình của bạn. Khi sử dụng struct thường xuyên hơn, bạn sẽ dần cảm thấy dễ dàng.
Comments
Post a Comment