Contents
Chào bạn đến với Fx Studio. Vào ngày 20/6/2020, tại sự kiện WWDC 2020 của Apple đã ra mắt phiên bản mới nhất của ngôn ngữ Swift. Đó là phiên bản Swift 5.3. Bây giờ, chúng ta sẽ tìm hiểu có những điểm nào mới trong Swift 5.3.
Lưu ý, bài viết này chỉ mang tính chất liệt kê những điểm thay đổi mới. Không trình bày hay giải thích ý nghĩa của chúng.
Chuẩn bị
-
- Xcode 12
- Playground
- Swift 5.3
Bạn cần chuẩn bị thêm một ít kiến thức cơ bản như là:
1. Multi-pattern catch clauses
Đố anh bắt được em!
Ở các phiên bản trước, thì mỗi catch
bạn chỉ được phép bắt 1 loại lỗi mà thôi. Nhưng đôi khi các anh lập trình viên lại lười xử lý các lỗi riêng biệt đó. Và Apple đã nghe lời thỉnh cầu của các dev, vì vậy tính năng này được ra mắt. Mục đích duy nhất là nhóm các lỗi lại với nhau, khi chúng nó có cùng chung 1 xử lý. Hoặc để cho mấy anh dev có cơ hội lười mà thôi.
Code ví dụ:
- Khai báo 1 error enum cho xịn sò
enum TemperatureError: Error { case tooCold, tooHot }
- Viết một function trả về nhiệt độ một cách ngẫu nhiên
func getReactorTemperature() -> Int { Int.random(in: 0...100) }
- Xử lý việc nhận được nhiệt độ đó, pha xử lý này được xem là kinh điển. Vì nó sẽ ném (throw) lỗi cho thèn nào gọi nó mà tiếp quản tiếp.
func checkReactorOperational() throws -> String { let temp = getReactorTemperature() if temp < 10 { throw TemperatureError.tooCold } else if temp > 90 { throw TemperatureError.tooHot } else { return "OK" } }
- Sử dụng nào!
do { let result = try checkReactorOperational() print("Result: \(result)") } catch TemperatureError.tooHot, TemperatureError.tooCold { print("Shut down the reactor!") } catch { print("An unknown error occurred.") }
Chú ý 2 trường hợp là tooHot
và tooCold
được dùng cùng trong 1 catch
.
2. Multiple trailing closures
Giúp các thanh niên chuyên dùng
closure
bớt phê cần đi.
Ví dụ ta có một function như sau:
func add(a: Int, b: Int, minResult: (Int) -> Void, maxResult: (Int) -> Void) { let minNumber = min(a,b) let maxNumber = max(a,b) minResult(minNumber) maxResult(maxNumber) }
Nếu như bạn đam mê dùng closure
thì có một vấn đề là: khi Xcode suggestion cho bạn và bạn chỉ biết enter và gõ code. Thì sử dụng function trên như sau (với Xcode 11 và Swift 5.2 trở về trước)
add(a: 10, b: 20, minResult: { (min) in print("Min : \(min)") }) { (max) in print("Max : \(max)") }
Loạn não khi 2 closure được đặt cuối cùng:
- 1 cái sẽ có tên đối số, là
minResult
- 1 cái sẽ bị lượt bỏ đi, vì nó là đứa cuối cùng trong danh sách tham số
Hậu quả là: “đọc code kiểu này nhiều thì loạn mắt”. Do đó, Swift 5.3 đã cải tiến như sau:
add(a: 10, b: 20) { (min) in print("Min : \(min)") } maxResult: { (max) in print("Max : \(max)") }
Bạn sẽ thấy:
- Nhận ra là cách mới sẽ thêm các closure ở phía sau một cách xin sò hơn
- Đỡ phải có dấu
,
để phân biệt giữa cách closure với nhau - Closure sau sẽ ở sau dấu
{
và trên cùng 1 hàng với dấu đó - Hiệu quả trong SwiftUI
- Lâu dần bạn sẽ quên mịa cách viết hàm mất đó
3. Synthesized Comparable conformance for enums
Kỉ nguyên bá đạo của Enum sẽ bắt đầu từ đây.
Bạn có thể so sánh các số Int, Float, Double … với nhau. Vì tụi nó có thể so sánh với nhau được. Còn với các struct, class, các đối tượng của nó muốn như vậy, thì phải implement protocol Comparable
. Tất nhiên, Enum được loại trừ ra như 1 quy luật tất yếu của cuộc sống. Khi bạn cố implement Comparable cho Enum thì bạn phải viết lại các toán tử (operator).
Quá cực!
Và Swift 5.3 đã cứu bạn thêm một lần nữa. Bây giờ, bạn có thể implement Comparable cho Enum một cách vô tư mà không cần thiết phải viết thêm gì hết.
Ví dụ cơ bản sau:
enum Size: Comparable { case small case medium case large case extraLarge } let shirtSize = Size.small let personSize = Size.large if shirtSize < personSize { print("That shirt is too small") }
Bạn có thể dùng các toán tử so sánh như <
, >
để so sánh trực tiếp các Enum với nhau. Và niềm vui lại nhân đôi, khi mục đích chính là bạn có thể dùng các hàm sorted
của hệ thống. Để sắp sếp đám Enum rắc rối kia.
Cái này thì nhiều ngôn ngữ lập trình khác phải ghen tỵ thôi. Ahihi!
enum WorldCupResult: Comparable { case neverWon case winner(stars: Int) } // cho các đội, khác nhau theo từng case hoặc giá trị trong mỗi case let americanMen = WorldCupResult.neverWon let americanWomen = WorldCupResult.winner(stars: 4) let japaneseMen = WorldCupResult.neverWon let japaneseWomen = WorldCupResult.winner(stars: 1) // tạo 1 mãng let teams = [americanMen, americanWomen, japaneseMen, japaneseWomen] // dùng hàm mặc định let sortedByWins = teams.sorted() // xem kết quả print(sortedByWins)
4. self
is no longer required in many places
Đối tượng tiêu diệt mới là
self
.
Nhiều bạn chắc đã từng gặp rất nhiều lần việc phải thêm từ khoá là self
. Để xác định cụ thể các thuộc tính và phương thức của class trong các closure. Nhất là chúng nó bất đồng bộ. Mà đôi khi bạn biết rằng, tụi nó cùng chung cấu trúc với nhau.
Với Swift 5.3, sẽ giảm đi cho bạn công việc này. Nó tự động suy đoán để biết được lúc nào cần
hay không cần
self trong code.
Ví dụ minh hoạ
class Test { var x = 0 func execute(_ work: @escaping () -> Void) { work() } func method() { execute(inc) // self is captured, but no error! execute { inc() } // error execute { self.x += 1 // with Swift 5.2 } } func inc() { x += 1 } }
Trong ví dụ trên,
- Trường hợp 1 thì mình sẽ bỏ qua con trỏ
self
trong việc gán hàminc
choexecute
. Vì Swift sẽ suy luận được việcinc
cùng chung cấu trúc vớiexcute
(mình không chắc chắn ở đâu lắm) - Sẽ báo lỗi, vì bây giờ mình cần thực thi
inc
trongexcute
- Cái cuối sẽ là các truyền thống, cần phải có con trỏ
self
để truy cập tớix
Nó không phải là
weak self
như truyền thuyết, nhưng phải cần thêm[self]
ở closure để capture self.
Một cách để dùng oke hơn và giúp Swift xác định rõ self, thì như sau:
execute { [self] in x += 1 // OKE with Swift 5.3 }
5. Type-Based Program Entry Points
Tạm biệt
main.swift
.
Không biết đây có chứa thuyết âm mưu nào từ Apple hay không. Khi bỏ đi vị thế độc tôn của file main.swift
. Và khi chạy một chương trình, thì con trỏ đầu tiên sẽ kích hoạt với bất kì class nào, miễn là có khai báo @main
và ở bất kì file *.swift
nào.
Có thể có nhiều bạn dev iOS không biết tới nó, vì @main
thường ẩn đi và bạn không bao giờ làm việc với nó, hay thấy được main.swift
. Nhưng cái tương tự như nó là : @UIApplicationMain
hay @NSApplicationMain
(bạn tìm tụi nó ở file AppDelegate.swift
trong iOS Project).
Còn với tính năng mới này, thì rất hay cho các bạn viết các ứng dụng như command-line
hoặc 1 một nền tảng mới của Swift mà Apple đang ấp ủ.
Ví dụ nào, tạo 1 project trên MacOS đơn giản nha:
- Bạn thấy
main.swift
- Mọi code sẽ bắt đầu ở đó
- Thực thi chương trình và thấy được kết quả ở console.
Xoá file main.swift
và thêm Main mới của bạn như sau:
Với @main
thì xác định class/struct nào sẽ được khởi tạo và tự động thực thi. Trong đó, phải có hàm static func main()
được cài đặt. Ngoài ra, ta có một số lưu ý sau:
- Không dùng
@main
trong filemain.swift
- Không có nhiều cái
@main
trong 1 project hay 1 file (nếu chương trình chỉ có 1 file) - Dùng ở base class và nó không được kế thừa hay có subclass
6. where
clauses on contextually generic declarations
Anh
where
bá đạo.
Trước đây, với Generic thì chúng ta không sử dụng được câu lệnh where
. Swift 5.3 đã thêm tính năng dùng câu lệnh where
với khai báo Generic.
Lợi điểm của việc này là bạn có thể sử dụng nó cho hàm có sẵn trong hệ thống (như: sorted() ). Implement với các protocol như: Equatable, Comparable
struct Stack<Element> { private var array = [Element]() mutating func push(_ obj: Element) { array.append(obj) } mutating func pop() -> Element? { array.popLast() } } extension Stack { func sorted() -> [Element] where Element: Comparable { array.sorted() } }
7. Enum cases as protocol witnesses
Enum bất diệt! Giờ ảnh không ngại bất cứ bố con thèn nào nữa rồi.
Lại là câu chuyên trước đây, khi bạn implement 1 Protocol vào 1 enum thì:
- Phải định nghĩa lại hết tất cả
- Với Struct và Class, có ý nghĩa hơn enum nhiều khi các thuộc tính và phương thức trong Protocol là kiểu
static
- Sử dụng tên Class/Struct để truy cập trực tiếp tới các thuộc tính/phương thức static đó
Còn với enum, thì hơi có vấn đề khi sử dụng như vậy. Vì việc tạo ra 1 enum từ 1 case của nó cũng chính là sử dụng tên Enum và .
tới case đó
Nó cũng như là mình sử dụng tên Class/Struct truy cập vào thuộc tính/phương thức
static
vậy.
Với Swift 5.3 thì
- Enum sẽ tự kết nối các
case
của nó với các thuộc tính/phương thứcstatic
của 1 Protocol mà nó implement - Yêu cầu là tụi nó phải trùng tên với nhau
Ví dụ cơ bản
/* - tên của các case khác tên của các thuộc tính/phương thức trong protocol */ protocol DecodingError { static var fileCorrupted: Self { get } static func keyNotFound(_ key: String) -> Self } enum JSONDecodingError: DecodingError { case _fileCorrupted case _keyNotFound(_ key: String) static var fileCorrupted: Self { return ._fileCorrupted } static func keyNotFound(_ key: String) -> Self { return ._keyNotFound(key) } }
Ví dụ không cơ bản
-
Tất cả trùng nhau và sử dụng tương đồng với nhau
-
Như là:
-
Int.defaultValue
-
Array.defaultValue
-
Dictionary.defaultValue
-
-
Thì enum cũng là : Padding.defaultValue
protocol Defaultable { static var defaultValue: Self { get } } // make integers have a default value of 0 extension Int: Defaultable { static var defaultValue: Int { 0 } } // make arrays have a default of an empty array extension Array: Defaultable { static var defaultValue: Array { [] } } // make dictionaries have a default of an empty dictionary extension Dictionary: Defaultable { static var defaultValue: Dictionary { [:] } } enum Padding: Defaultable { case pixels(Int) case cm(Int) case defaultValue } let temp = Padding.defaultValue print(temp)
8. Refine didSet
Semantics
Mày không thoát được đâu con trai. Tu bi cơn ti niu!
Swift 5.3, cung cấp thêm cho bạn một cơ hội quay đầu khi đã lỡ tay gán giá trị mới cho 1 biến. Với property observers là didSet
thì được cung cấp thêm 1 biến có tên là oldValue
. Nó lưu trữ giá trị trước đó của biến. Giúp bạn truy xuất tới được giá trị trước và nếu bạn không dùng thì nó sẽ không được truy xuất ra.
Việc này có ý nghĩa với từng bài toán cụ thể mà bạn đang làm. Còn đây là 1 ví dụ cơ bản để minh hoạ cho cách dùng của nó.
class A1 { var firstArray = [1,2,3] { didSet { print("didSet called") } } var secondArray = [1,2,3,4] { didSet { print(oldValue) } } } let a1 = A1() // This will not call the getter to fetch the oldValue of firstArray a1.firstArray = [1,2] // This will call the getter to fetch the oldValue of secondArray a1.secondArray = [1] print(a1.secondArray)
9. A new Float16
type
Thêm bạn thêm vui!
Thế giới chào đón thêm em Float16
vào gia đình nhà Float với Swift 5.3. Ngoài ra, còn liệt kê thêm các em Float khác cho bạn biết thêm.
let first: Float16 = 5 let second: Float32 = 11 let third: Float64 = 7 let fourth: Float80 = 13
Tham khảo
- Swift Programming Language Evolution
- What’s new in Swift 5.3? (Hacking With Swift)
OKAY! Mình xin tạm kết bài viết này ở đây. Nếu có thắc mắc hoặc góp ý gì, thì bạn có thể để lại comment hoặc contact tới mình. Cảm ơn bạn đã được bài viết này!
Thân ái và quyết thắng!
Related Posts:
Written by chuotfx
Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!
Leave a Reply Cancel reply
Fan page
Tags
Recent Posts
- Phù thủy phiên dịch ý tưởng
- XML Delimiters – Mở khóa thế giới prompt phức tạp
- Instructions – Cung cấp hướng dẫn cho các Gen AI
- SMART – Hướng dẫn dành tạo Prompt cho người mới bắt đầu
- Nhìn lại năm 2024
- CO-STAR – Công thức vàng để viết Prompt hiệu quả cho LLM
- Prompt Engineering trong 10 phút
- Một số ví dụ sử dụng Prompt cơ bản khi làm việc với AI
- Prompt trong 10 phút
- Charles Proxy – Phần 1 : Giới thiệu, cài đặt và cấu hình
You may also like:
Archives
- January 2025 (5)
- December 2024 (4)
- September 2024 (1)
- July 2024 (1)
- June 2024 (1)
- May 2024 (4)
- April 2024 (2)
- March 2024 (5)
- January 2024 (4)
- February 2023 (1)
- January 2023 (2)
- November 2022 (2)
- October 2022 (1)
- September 2022 (5)
- August 2022 (6)
- July 2022 (7)
- June 2022 (8)
- May 2022 (5)
- April 2022 (1)
- March 2022 (3)
- February 2022 (5)
- January 2022 (4)
- December 2021 (6)
- November 2021 (8)
- October 2021 (8)
- September 2021 (8)
- August 2021 (8)
- July 2021 (9)
- June 2021 (8)
- May 2021 (7)
- April 2021 (11)
- March 2021 (12)
- February 2021 (3)
- January 2021 (3)
- December 2020 (3)
- November 2020 (9)
- October 2020 (7)
- September 2020 (17)
- August 2020 (1)
- July 2020 (3)
- June 2020 (1)
- May 2020 (2)
- April 2020 (3)
- March 2020 (20)
- February 2020 (5)
- January 2020 (2)
- December 2019 (12)
- November 2019 (12)
- October 2019 (19)
- September 2019 (17)
- August 2019 (10)