Swift là ngôn ngữ lập trình của Apple kết hợp sức mạnh của Objective-C với sự an toàn của các ngôn ngữ hiện đại. Không giống nhiều ngôn ngữ khác, Swift cung cấp các cấu trúc độc đáo: kiểu giá trị với sao chép tự động, Actors với cách ly ở cấp trình biên dịch, Property Wrappers để tái sử dụng logic thuộc tính và Lập trình Hướng Giao thức như một giải pháp thay thế cho kế thừa cổ điển. Theo Tài liệu Apple Swift, các tính năng này làm cho mã an toàn hơn và hiệu quả hơn.
Điểm Chính
Trong Swift, struct là kiểu giá trị, trong khi class là kiểu tham chiếu. Sự khác biệt cơ bản này quyết định hành vi khi gán, truyền vào hàm và quản lý bộ nhớ. Struct được sao chép toàn bộ mỗi khi gán, class được truyền theo tham chiếu — chỉ con trỏ đến đối tượng trên heap được sao chép.
Struct tự động triển khai ngữ nghĩa giá trị — mỗi thể hiện độc lập. Thay đổi thuộc tính struct chỉ khả thi qua var, và các phương thức sửa đổi thuộc tính phải được đánh dấu mutating. Struct không hỗ trợ kế thừa nhưng có thể tuân thủ giao thức. Thư viện chuẩn của Swift sử dụng struct cho String, Array, Dictionary, Int, Bool — tất cả các kiểu cơ bản đều là kiểu giá trị.
struct User {
let id: Int
var name: String
mutating func updateName(_ newName: String) {
name = newName
}
}
var user1 = User(id: 1, name: "Alice")
var user2 = user1
user2.updateName("Bob")
// user1.name === "Alice", user2.name === "Bob"
// Sao chép hoàn toàn — thể hiện độc lập
Class được lưu trữ trên heap và hỗ trợ so sánh tham chiếu. Nhiều biến có thể tham chiếu đến cùng một đối tượng, thay đổi qua một tham chiếu sẽ hiển thị qua tất cả các tham chiếu khác. Class hỗ trợ kế thừa, cho phép ghi đè phương thức (override), có deinit để dọn dẹp tài nguyên. Trong Swift, class được sử dụng ít hơn struct — Apple khuyên bắt đầu với struct và chỉ chuyển sang class khi cần kế thừa hoặc ngữ nghĩa tham chiếu.
class Car {
let model: String
var speed: Int
init(model: String, speed: Int) {
self.model = model
self.speed = speed
}
func accelerate(_ amount: Int) {
speed += amount
}
}
let car1 = Car(model: "Tesla", speed: 0)
let car2 = car1
car2.accelerate(50)
// car1.speed === 50, car2.speed === 50
// Cùng tham chiếu — thay đổi hiển thị ở mọi nơi
Việc chọn giữa struct và class phụ thuộc vào ngữ nghĩa dữ liệu. Cho cấu hình, mô hình dữ liệu và trạng thái hãy dùng struct — sao chép an toàn và dễ dự đoán hơn. Cho đối tượng có định danh (phiên người dùng, kết nối cơ sở dữ liệu) và khi cần kế thừa, hãy dùng class.
Actor là kiểu tham chiếu bảo vệ trạng thái có thể thay đổi khỏi truy cập đồng thời. Trình biên dịch Swift đảm bảo rằng các biến actor chỉ có thể được truy cập từ cùng ngữ cảnh actor — bất kỳ truy cập bên ngoài nào gây ra lỗi biên dịch. Điều này ngăn chặn điều kiện đua ở cấp ngôn ngữ, không cần khóa thủ công.
Tất cả thuộc tính và phương thức của actor đều được cách ly theo mặc định. Gọi phương thức actor từ bên ngoài yêu cầu await — Swift chuyển thực thi sang actor và quay lại. Actor đồng bộ hóa truy cập tuần tự: yêu cầu từ các tác vụ khác nhau được xếp hàng đợi. Điều này đảm bảo trạng thái nhất quán bên trong actor. Các phương thức không cách ly (nonisolated) có thể được gọi không cần await nhưng không thể truy cập thuộc tính có thể thay đổi.
actor BankAccount {
private var balance: Double
init(initialBalance: Double) {
balance = initialBalance
}
func deposit(amount: Double) {
balance += amount
}
func withdraw(amount: Double) throws {
guard balance >= amount else {
throw BankError.insufficientFunds
}
balance -= amount
}
nonisolated func accountInfo() -> String {
return "Tài khoản Ngân hàng"
}
}
let account = BankAccount(initialBalance: 1000)
await account.deposit(amount: 500)
// balance = 1500 — nguyên tử, không có điều kiện đua
Actor hữu ích cho trình quản lý trạng thái, bộ nhớ đệm và dịch vụ mạng nơi nhiều tác vụ đồng thời đọc và ghi dữ liệu. Không giống đồng bộ hóa thủ công qua NSLock hoặc DispatchQueue, actor được kiểm tra tại thời điểm biên dịch — không thể mắc lỗi. Lỗi cách ly xuất hiện tại thời điểm biên dịch, không phải thời điểm chạy.
Property Wrapper là chú thích thêm hành vi vào thuộc tính mà không thay đổi khai báo của nó. SwiftUI và Combine cung cấp các wrapper tích hợp để quản lý trạng thái view. Trình biên dịch tạo mã bao bọc truy cập thuộc tính, thêm logic quan sát, đồng bộ hóa hoặc chuyển đổi.
@Published được dùng trong các lớp tuân thủ ObservableObject để tự động công bố thay đổi. Người đăng ký Combine nhận thông báo mỗi khi cập nhật. @State là trạng thái local của view SwiftUI. SwiftUI tự động vẽ lại view khi @State thay đổi. @Binding tạo kết nối hai chiều giữa view cha và con mà không sở hữu dữ liệu.
import SwiftUI
import Combine
@main
struct CounterApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
class CounterViewModel: ObservableObject {
@Published var count: Int = 0
func increment() {
count += 1
}
}
struct ContentView: View {
@State private var greeting: String = "Xin chào"
@StateObject private var viewModel = CounterViewModel()
var body: some View {
VStack {
CounterDisplay(count: $viewModel.count)
Text(greeting)
Button("Tăng", action: viewModel.increment)
}
}
}
struct CounterDisplay: View {
@Binding var count: Int
var body: some View {
Text("Đếm: \(count)")
}
}
Property Wrappers tùy chỉnh được tạo bằng @propertyWrapper và một cấu trúc với trường wrappedValue. Điều này cho phép tái sử dụng logic: xác thực, chuẩn hóa, lưu trữ UserDefaults. Property Wrappers giảm đáng kể mã mẫu và làm rõ ý định của nhà phát triển thông qua chú thích.
@propertyWrapper
struct Clamped<T: Comparable> {
private var value: T
let range: ClosedRange<T>
init(wrappedValue: T, range: ClosedRange<T>) {
self.range = range
self.value = min(max(wrappedValue, range.lowerBound), range.upperBound)
}
var wrappedValue: T {
get { value }
set { value = min(max(newValue, range.lowerBound), range.upperBound) }
}
}
struct Settings {
@Clamped(range: 0...100) var volume: Int = 50
}
var s = Settings()
s.volume = 150
// s.volume === 100 — giá trị tự động bị giới hạn
Generics là cơ chế mạnh mẽ của Swift để viết mã linh hoạt mà không mất an toàn kiểu. Các hàm và kiểu generic hoạt động với bất kỳ kiểu nào đáp ứng ràng buộc. Lập trình Hướng Giao thức (POP) là mô hình Swift nơi protocol + extension thay thế kế thừa lớp cổ điển, cung cấp thành phần hành vi.
Swift cho phép ràng buộc tham số generic với giao thức (mệnh đề where). Giao thức với kiểu liên kết là tương đương generic cho giao thức: kiểu cụ thể được xác định tại thời điểm tuân thủ. Đây là nền tảng cho các bộ sưu tập và thuật toán an toàn kiểu. Thư viện chuẩn Swift sử dụng Generics tích cực: Array, Dictionary, Optional — tất cả đều là kiểu generic.
protocol Cacheable {
associatedtype Key: Hashable
associatedtype Value
func get(_ key: Key) -> Value?
mutating func set(_ value: Value, for key: Key)
}
struct MemoryCache<K: Hashable, V>: Cacheable {
private var storage: [K: V] = [:]
typealias Key = K
typealias Value = V
func get(_ key: K) -> V? {
return storage[key]
}
mutating func set(_ value: V, for key: K) {
storage[key] = value
}
}
func firstMatch<T: Equatable>(in array: [T], target: T) -> Int? {
for (index, item) in array.enumerated() {
if item == target {
return index
}
}
return nil
}
let numbers = [10, 20, 30, 40]
let index = firstMatch(in: numbers, target: 30)
// index === 2 — một phương thức generic cho tất cả kiểu Equatable
Extension giao thức là cơ chế chính của POP. Trong extension có thể cung cấp triển khai mặc định cho các phương thức giao thức. Các kiểu tuân thủ giao thức nhận được triển khai này miễn phí và có thể ghi đè nếu cần. Điều này cho phép xây dựng hệ thống phân cấp hành vi mà không cần kế thừa lớp — giải pháp thay thế cho đa kế thừa, không có trong Swift.
protocol Drivable {
var speed: Double { get set }
func drive()
}
extension Drivable {
func drive() {
if speed > 0 {
print("Lái xe ở tốc độ \(speed) km/h")
}
}
}
struct Bicycle: Drivable {
var speed: Double = 15
}
struct Plane: Drivable {
var speed: Double = 900
func drive() {
print("Bay ở tốc độ \(speed) km/h")
}
}
let bike = Bicycle()
bike.drive() // "Lái xe ở tốc độ 15.0 km/h" — mặc định
let plane = Plane()
plane.drive() // "Bay ở tốc độ 900.0 km/h" — ghi đè
POP với extension cho phép thành phần qua nhiều giao thức — một kiểu có thể tuân thủ nhiều giao thức, nhận triển khai của chúng. Điều này linh hoạt hơn kế thừa đơn lớp nơi toàn bộ hệ thống phân cấp cố định. Thư viện chuẩn Swift được xây dựng trên POP: CustomStringConvertible, Equatable, Hashable, Codable — tất cả đều là giao thức với extension.
Enum trong Swift là cấu trúc mạnh mẽ hơn đáng kể so với các ngôn ngữ khác. Mỗi trường hợp có thể có giá trị liên kết — một tập dữ liệu tùy ý thuộc bất kỳ kiểu nào. Kết hợp với extension và pattern matching, enum trở thành nền tảng để mô hình hóa trạng thái, kết quả hoạt động và máy trạng thái hữu hạn.
Giá trị liên kết cho phép mỗi trường hợp lưu trữ dữ liệu duy nhất. Ví dụ, trường hợp loading không chứa dữ liệu, success chứa kết quả, failure chứa lỗi. Pattern matching qua switch trích xuất giá trị liên kết và xử lý từng trường hợp. Trình biên dịch kiểm tra tính đầy đủ (exhaustive switch) — điều này loại bỏ các trạng thái bị quên.
enum NetworkResult<T> {
case loading
case success(T)
case failure(Error)
}
enum MediaFile {
case image(url: URL, width: Int, height: Int)
case video(url: URL, duration: Double)
case audio(url: URL, bitrate: Int)
}
func handle(_ result: NetworkResult<String>) {
switch result {
case .loading:
print("Đang tải...")
case .success(let data):
print("Đã nhận: \(data)")
case .failure(let error):
print("Lỗi: \(error.localizedDescription)")
}
}
let file = MediaFile.video(url: URL(string: "https://example.com/video.mp4")!, duration: 120)
if case .video(let url, let duration) = file {
print("Video \(url) thời lượng \(duration)s")
}
Swift cho phép thêm phương thức, thuộc tính tính toán và tuân thủ giao thức qua extension cho enum. Điều này không yêu cầu thay đổi khai báo gốc — mã vẫn sạch và mô-đun. Các phương thức trong extension có thể làm việc với giá trị liên kết, tính toán dữ liệu dẫn xuất và triển khai logic nghiệp vụ.
extension MediaFile {
var fileName: String {
switch self {
case .image(let url, _, _),
.video(let url, _),
.audio(let url, _):
return url.lastPathComponent
}
}
var description: String {
switch self {
case .image(_, let w, let h):
return "Hình ảnh \(w)x\(h)"
case .video(_, let d):
return "Video \(d)s"
case .audio(_, let b):
return "Âm thanh \(b) kbps"
}
}
}
extension MediaFile: Equatable {
static func == (lhs: MediaFile, rhs: MediaFile) -> Bool {
return lhs.fileName == rhs.fileName
}
}
let files: [MediaFile] = [file]
files.forEach { print($0.description) }
// "Video 120.0s"
Enum với giá trị liên kết + extension là sự thay thế cho lớp sealed từ Kotlin hoặc union phân biệt từ các ngôn ngữ khác. Swift sử dụng kết hợp này cho Result (kiểu chuẩn trong thư viện chuẩn), Optional (Optional là enum với .none và .some) và xử lý trạng thái trong SwiftUI. Pattern matching đảm bảo tất cả trạng thái được xử lý — lỗi thời gian chạy là không thể.
Câu hỏi Thường gặp
Struct — kiểu giá trị, sao chép khi gán, không hỗ trợ kế thừa. Class — kiểu tham chiếu, truyền theo tham chiếu, hỗ trợ kế thừa, deinit và kiểm tra định danh (===). Apple khuyên bắt đầu với struct và chỉ chuyển sang class khi cần ngữ nghĩa tham chiếu.
Actor — kiểu tham chiếu an toàn luồng. Trình biên dịch ngăn chặn truy cập đồng thời vào trạng thái có thể thay đổi từ các ngữ cảnh khác nhau. Sử dụng Actor cho trình quản lý trạng thái, bộ nhớ đệm và dịch vụ được nhiều tác vụ truy cập. Gọi phương thức actor yêu cầu await.
Chính: @State — trạng thái view cục bộ; @Binding — kết nối dữ liệu không sở hữu; @Published — công bố thay đổi trong ObservableObject; @StateObject — sở hữu ObservableObject; @EnvironmentObject — phụ thuộc từ môi trường. Wrapper tùy chỉnh tạo qua @propertyWrapper.
POP — mô hình nơi protocol + extension định nghĩa hành vi thay vì kế thừa lớp. Một kiểu tuân thủ nhiều giao thức, nhận triển khai miễn phí. Đây là thành phần thay vì kế thừa — linh hoạt và an toàn hơn. Thư viện chuẩn Swift (Equatable, Codable) được xây dựng trên POP.
Enum trong Swift hỗ trợ giá trị liên kết — mỗi trường hợp có thể lưu trữ dữ liệu tùy ý thuộc các kiểu khác nhau. Kết hợp với pattern matching (switch), điều này cung cấp mô hình mạnh mẽ cho trạng thái, lỗi và Thiết kế Hướng Miền. Optional và Result là enum chuẩn Swift với giá trị liên kết.
Tổng kết
Chúng tôi sẽ phát triển ứng dụng di động chìa khóa trao tay
IT Sectr tạo các ứng dụng iOS và Android cho các công ty khởi nghiệp và doanh nghiệp từ năm 2017. Chúng tôi sẽ tư vấn và đề xuất giải pháp tốt nhất cho bạn.