PINCache là thư viện cache đối tượng song song, nhanh và tránh deadlock dành cho iOS, tvOS và macOS. Nó được thiết kế để xử lý các tài nguyên đắt giá như dữ liệu tải về hoặc kết quả xử lý chậm. Là phiên bản cải tiến từ TMCache, PINCache đã tái cấu trúc hoàn toàn hệ thống cache, giải quyết triệt để vấn đề deadlock trong các tình huống đồng thời cao, mang đến giải pháp cache ổn định và hiệu quả cho ứng dụng di động.
Kiến trúc và ưu điểm cốt lõi của PINCache
PINCache sử dụng kiến trúc phân tầng, bao gồm cache bộ nhớ (PINMemoryCache) và cache đĩa (PINDiskCache), hoạt động như hai lớp lưu trữ tự tương tự. Thiết kế này tận dụng tối đa sức mạnh của GCD (Grand Central Dispatch) để đảm bảo truy cập đa luồng an toàn đồng thời cho phép các thao tác song song thực sự.
Sự phối hợp giữa cache bộ nhớ và cache đĩa
Cache bộ nhớ cung cấp tốc độ truy cập mili-giây, trong khi cache đĩa đảm bảo lưu trữ dữ liệu bền vững. Khi ứng dụng nhận cảnh báo bộ nhớ hoặc chuyển vào chế độ nền, PINMemoryCache tự động dọn dẹp các đối tượng trong bộ nhớ. Ngược lại, các đối tượng trong PINDiskCache vẫn được giữ lại cho đến khi bạn xóa thủ công hoặc đạt đến giới hạn dung lượng/thời gian đã đặt.
Các tính năng chính bao gồm:
- Thiết kế tránh deadlock: Giải quyết triệt để vấn đề deadlock trong các tình huống đồng thời cao của TMCache.
- Hoạt động song song: Mọi thao tác đọc/ghi đều hỗ trợ thực thi song song, không chặn luồng chính.
- An toàn luồng: Dựa trên GCD, đảm bảo truy cập đa luồng hoàn toàn an toàn.
- Tương thích NSCoding: Hỗ trợ mọi đối tượng tuân thủ giao thức NSCoding.
- Chiến lược dọn dẹp thông minh: Hỗ trợ các thuật toán loại bỏ LRU (Ít được sử dụng nhất) và LFU (Ít thường xuyên nhất).
Hướng dẫn cài đặt và tích hợp
Cài đặt bằng CocoaPods
Thêm dòng sau vào Podfile:
pod 'PINCache'
Chạy lệnh pod install.
Cài đặt bằng Carthage
Thêm dòng sau vào Cartfile:
github "pinterest/PINCache"
Chạy lệnh carthage update --platform ios.
Cài đặt bằng Swift Package Manager
Trong Xcode, thêm gói qua URL: https://github.com/pinterest/PINCache.git.
Các trường hợp ứng dụng và thực hành tốt nhất
Tối ưu hóa cache hình ảnh
Trong tình huống tải hình ảnh, PINCache có thể cải thiện đáng kể trải nghiệm người dùng:
// Cache hình ảnh
let image = UIImage(data: imageData, scale: UIScreen.main.scale)
PINCache.shared().setObject(image, forKey: "user_avatar")
// Lấy hình ảnh bất đồng bộ
PINCache.shared().object(forKey: "user_avatar") { (cache, key, object) in
if let cachedImage = object as? UIImage {
DispatchQueue.main.async {
imageView.image = cachedImage
}
}
}
Lưu trữ đối tượng collection hiệu quả
Nhờ NSKeyedArchiver, các đối tượng trùng lặp trong collection chỉ chiếm một instance trên đĩa:
NSArray *images = @[image, image, image];
[[PINCache sharedCache] setObject:images forKey:@"gallery_images"];
NSLog(@"3 ảnh chỉ tốn dung lượng của 1 ảnh: %lld", [[[PINCache sharedCache] diskCache] byteCount]);
Quá trình phát triển: Từ TMCache đến PINCache
Giải quyết triệt để vấn đề deadlock
Vấn đề chính của TMCache nằm ở thiết kế cơ chế khóa chưa hoàn thiện, dễ gây deadlock trong môi trường đồng thời cao. PINCache đã giải quyết hoàn toàn vấn đề này bằng các cách sau:
- Chiến lược khóa chi tiết: Thiết kế cơ chế khóa độc lập cho cache bộ nhớ và cache đĩa.
- Hàng đợi thao tác bất đồng bộ: Mọi thao tác tốn thời gian đều được thực thi trong hàng đợi nền.
- Tránh gọi đệ quy: Thiết kế lại chuỗi gọi API để ngăn chặn chờ đợi vòng lặp.
Điểm nổi bật về tối ưu hiệu năng
Xem mã nguồn PINCache.m và PINDiskCache.m, có thể thấy nhiều tối ưu hiệu năng:
- Tối ưu URL: Sử dụng
NSURLIsExcludedFromBackupKeyđể tránh kiểm tra thư mục, cải thiện hiệu năng đáng kể. - Tải metadata trễ: Khi trả về đối tượng TTL cache, không chờ đọc toàn bộ metadata.
- Dọn dẹp ưu tiên thấp: Dọn dẹp cache bộ nhớ và loại bỏ đối tượng hết hạn sử dụng hàng đợi ưu tiên thấp.
Hướng dẫn cấu hình và tinh chỉnh
Cấu hình cache bộ nhớ
let memoryCache = PINMemoryCache.shared()
memoryCache.ageLimit = 60 * 60 // 1 giờ
memoryCache.costLimit = 50 * 1024 * 1024 // 50 MB
memoryCache.removeAllObjectsOnMemoryWarning = true
memoryCache.removeAllObjectsOnEnteringBackground = true
Cấu hình cache đĩa
let diskCache = PINDiskCache(name: "MyAppCache")
diskCache.byteLimit = 100 * 1024 * 1024 // 100 MB
diskCache.ageLimit = 7 * 24 * 60 * 60 // 7 ngày
diskCache.ttlCache = true // Bật TTL cache
Lưu ý và thực hành tốt nhất
Nguyên tắc vàng để tránh deadlock
Mặc dù PINCache đã giải quyết vấn đề deadlock của TMCache, vẫn cần lưu ý:
- Không gọi phương thức đồng bộ trong callback: Gọi phương thức cache đồng bộ trong block callback có thể gây deadlock.
- Đặt số lượng thao tác đồng thời hợp lý: Sử dụng
maxConcurrentOperationsđể kiểm soát mức độ đồng thời. - Dọn dẹp dữ liệu hết hạn kịp thời: Thường xuyên kiểm tra và xóa cache hết hạn để tránh lãng phí dung lượng đĩa.
Kiểm thử và gỡ lỗi
Dự án bao gồm bộ kiểm thử hoàn chỉnh trong thư mục Tests/. Chạy các bài kiểm thử để đảm bảo PINCache hoạt động chính xác trong môi trường của bạn.
Lịch sử phiên bản và hướng phát triển
Các mốc phiên bản quan trọng
- 3.0.4 (2024-05-13): Bổ sung cấu hình số lượng thao tác đồng thời tối đa và chiến lược loại bỏ LFU.
- 3.0.1 (2020-08-20): Hỗ trợ Catalyst và Xcode 12.
- 2.2 (2016-02-10): Giới thiệu hỗ trợ TTL cache.
- 2.0 (2015-02-25): Thiết kế lại kiến trúc, giải quyết triệt để vấn đề deadlock.
Bảo trì liên tục và đóng góp từ cộng đồng
PINCache được duy trì bởi nhóm Pinterest, có cộng đồng hoạt động tích cực và cập nhật liên tục. Xem CHANGELOG.md để biết lịch sử phiên bản đầy đủ và chi tiết cải tiến.
PINCache, với tư cách là sự thay thế hiện đại cho TMCache, đã giải quyết triệt để vấn đề deadlock thông qua kiến trúc được thiết kế lại, đồng thời duy trì khả năng tương thích ngược với API. Dù xử lý cache hình ảnh, cache phản hồi mạng hay lưu trữ đối tượng phức tạp, PINCache đều cung cấp giải pháp ổn định, hiệu quả và an toàn luồng.