1. Bản chất của Optional trong Swift
Trong Swift, Optional thực chất là một kiểu dữ liệu enum được định nghĩa trong thư viện chuẩn. Nó sử dụng Generic để bao bọc một giá trị hoặc biểu thị sự vắng mặt của giá trị đó thông qua hai trường hợp: none và some.
public enum Optional<Wrapped> : ExpressibleByNilLiteral {
case none
case some(Wrapped)
public init(_ some: Wrapped)
}
Khi chúng ta khai báo một biến Optional, thực chất là đang sử dụng cú pháp rút gọn (syntax sugar) của enum này. Dưới đây là các cách viết tương đương:
// Cách viết thông thường
var level: Int? = 5
level = nil
// Cách viết tường minh bản chất Enum
var level1: Optional<Int> = .some(5)
var level2: Optional<Int> = .none
// Sử dụng Switch để kiểm tra Optional
let score: Int? = 100
switch score {
case let val?:
print("Giá trị là: \(val)")
case nil:
print("Không có giá trị")
}
2. Toán tử tràn số (Overflow Operators)
Mặc định, Swift sẽ gây ra lỗi runtime nếu một phép toán số học vượt quá phạm vi lưu trữ của kiểu dữ liệu. Tuy nhiên, trong một số trường hợp xử lý bit hoặc thuật toán đặc thù, chúng ta cần hành vi "vòng lặp" (wrap around). Swift cung cấp các toán tử bắt đầu bằng dấu & để xử lý việc này.
&+: Cộng tràn số.&-: Trừ tràn số.&*: Nhân tràn số.
var maxVal = UInt8.max // 255
maxVal = maxVal &+ 1 // Kết quả sẽ quay về 0 thay vì gây crash
var minVal = UInt8.min // 0
minVal = minVal &- 1 // Kết quả sẽ là 255
3. Nạp chồng toán tử (Operator Overload)
Swift cho phép các lớp (class), cấu trúc (struct) và enum cung cấp các triển khai tùy chỉnh cho những toán tử hiện có. Điều này giúp mã nguồn trở nên ngắn gọn và dễ đọc hơn.
struct Vector2D {
var x = 0.0, y = 0.0
// Nạp chồng toán tử cộng cho 2 Vector
static func + (left: Vector2D, right: Vector2D) -> Vector2D {
return Vector2D(x: left.x + right.x, y: left.y + right.y)
}
// Toán tử một ngôi (Prefix)
static prefix func - (vector: Vector2D) -> Vector2D {
return Vector2D(x: -vector.x, y: -vector.y)
}
// Toán tử gán kết hợp
static func += (left: inout Vector2D, right: Vector2D) {
left = left + right
}
}
let v1 = Vector2D(x: 1.0, y: 2.0)
let v2 = Vector2D(x: 3.0, y: 4.0)
let combined = v1 + v2 // Vector2D(x: 4.0, y: 6.0)
4. Equatable và Comparable
Để so sánh hai đối tượng, chúng ta cần triển khai các protocol chuẩn của Swift.
- Equatable: Cho phép sử dụng toán tử
==và!=. Swift tự động tạo triển khai này cho struct nếu các thuộc tính của nó đều thỏa mãn Equatable. - Comparable: Cho phép sử dụng các toán tử so sánh như
<,>,<=,>=.
struct Employee: Comparable {
var id: Int
var rank: Int
static func == (lhs: Employee, rhs: Employee) -> Bool {
return lhs.id == rhs.id
}
static func < (lhs: Employee, rhs: Employee) -> Bool {
return lhs.rank < rhs.rank
}
}
5. Tùy chỉnh toán tử mới (Custom Operators)
Ngoài việc nạp chồng các toán tử có sẵn, bạn có thể định nghĩa các toán tử hoàn toàn mới với các ký hiệu tự chọn.
// Khai báo toán tử trung tố mới
infix operator ^^: ExponentiationPrecedence
precedencegroup ExponentiationPrecedence {
associativity: right
higherThan: MultiplicationPrecedence
}
func ^^ (base: Double, power: Double) -> Double {
return pow(base, power)
}
let result = 2.0 ^^ 3.0 // 8.0
6. Kỹ thuật Extension (Mở rộng)
Extension cho phép thêm chức năng mới vào một kiểu dữ liệu đã tồn tại (ngay cả khi bạn không có mã nguồn gốc của kiểu đó). Extension có thể thêm phương thức, thuộc tính tính toán, subscript, và làm cho kiểu dữ liệu tuân thủ protocol.
// Mở rộng Array để truy cập phần tử an toàn
extension Array {
subscript(safe index: Int) -> Element? {
return indices.contains(index) ? self[index] : nil
}
}
let list = [10, 20, 30]
print(list[safe: 5] ?? "Không tồn tại")
// Thêm phương thức vào kiểu Int
extension Int {
func squared() -> Int {
return self * self
}
}
let num = 4.squared() // 16
7. Extension và Protocol
Chúng ta thường sử dụng Extension để phân tách các phần triển khai protocol, giúp mã nguồn sạch sẽ hơn. Ngoài ra, Extension còn có thể cung cấp các triển khai mặc định cho protocol.
protocol Workable {
func doWork()
}
extension Workable {
func doWork() {
print("Đang xử lý công việc mặc định...")
}
}
class Developer: Workable {
// Không bắt buộc phải viết lại doWork() nhờ extension trên
}
// Ràng buộc Extension với điều kiện (Conditional Conformance)
extension Array where Element: Equatable {
func allEqual() -> Bool {
guard let first = self.first else { return true }
return self.allSatisfy { $0 == first }
}
}
Lưu ý quan trọng: Extension không thể ghi đè (override) các phương thức hiện có và không thể thêm các thuộc tính lưu trữ (stored properties). Nếu một class tuân thủ protocol yêu cầu initializer trong extension, initializer đó phải được đánh dấu là convenience.