内存泄露、网络加密与通用缓存

7 阅读5分钟

一、内存泄露排查:循环引用检测与修复

内存泄露的本质是对象不再被需要,但引用计数无法归零,导致内存无法释放。

🔧 核心排查工具

  • Xcode Memory Graph Debugger:运行时可视化对象引用关系,紫色!图标标识潜在泄露
  • Instruments Leaks工具:实时监控泄露对象,定位未释放的CF对象
  • 静态代码分析:检查闭包、代理、通知等常见泄露场景

💡 高频泄露场景与修复

// 1. 闭包循环引用(常见于网络回调)
class TransactionManager {
    var completion: ((Bool) -> Void)?
    
    func processTransaction(amount: Double) {
        // ❌ 错误:闭包强持有self
        APIClient.sendTransaction(amount: amount) { success in
            self.updateUI() // 形成循环引用
        }
        
        // ✅ 正确:使用weak打破循环
        APIClient.sendTransaction(amount: amount) { [weak self] success in
            self?.updateUI()
        }
    }
}

// 2. 代理强引用
class AccountViewController {
    // ❌ 错误:强引用代理
    var delegate: AccountDelegate?
    
    // ✅ 正确:弱引用代理
    weak var delegate: AccountDelegate?
}

// 3. 定时器未正确销毁
class TimerHandler {
    var timer: Timer?
    
    deinit {
        // ✅ 必须手动invalidate
        timer?.invalidate()
        timer = nil
    }
}

二、网络数据加密:金融级安全传输方案

APP对数据传输安全有极高要求需要多层加密保护。

🔐 多层次加密架构

graph TD
    A[客户端请求] --> B[应用层加密]
    B --> C[传输层加密 HTTPS]
    C --> D[服务端接收]
    
    subgraph "应用层加密细节"
        B1[敏感参数单独加密] --> B2[参数排序与签名]
        B2 --> B3[加入时间戳/随机数]
    end

🛡️ 核心加密实践

1. 传输层安全(基础)

// ATS强制HTTPS(Info.plist配置)
<key>NSAppTransportSecurity</key>
<dict>
    <key>NSAllowsArbitraryLoads</key>
    <false/> <!-- 生产环境必须为false -->
</dict>

// SSL Pinning防中间人攻击
let session = URLSession(configuration: .default, 
                        delegate: SSLPinningDelegate(), 
                        delegateQueue: nil)

2. 应用层加密(增强)

// 敏感数据RSA加密示例
func encryptSensitiveData(_ data: String) -> String? {
    // 1. 从服务端获取RSA公钥(首次启动或定期更新)
    guard let publicKey = KeychainManager.getRSAPublicKey() else {
        return nil
    }
    
    // 2. 加密密码、验证码等敏感字段
    let encryptedPassword = RSA.encrypt(data, publicKey: publicKey)
    
    // 3. 生成请求签名
    let timestamp = Int(Date().timeIntervalSince1970)
    let nonce = UUID().uuidString
    let signString = "\(data)\(timestamp)\(nonce)\(API_SECRET)"
    let signature = HMAC.sha256(signString)
    
    return encryptedPassword
}

// 请求参数签名示例
struct SecureRequest {
    let userId: String
    let encryptedData: String
    let timestamp: Int
    let nonce: String
    let signature: String
    
    init(userId: String, sensitiveData: String) {
        self.userId = userId
        self.timestamp = Int(Date().timeIntervalSince1970)
        self.nonce = UUID().uuidString
        
        // 加密敏感数据
        self.encryptedData = RSA.encrypt(sensitiveData)
        
        // 生成签名(防篡改)
        let params = "userId=\(userId)&data=\(encryptedData)×tamp=\(timestamp)&nonce=\(nonce)"
        self.signature = HMAC.sha256(params + API_SECRET)
    }
}

3. 密钥安全管理

  • 禁止硬编码:绝不将密钥直接写在代码中
  • Keychain存储:使用iOS Keychain存储加密密钥
  • 动态获取:敏感密钥从服务端动态获取,可定期轮换

📊 加密方案选择指南

场景推荐方案说明
登录密码/支付密码RSA非对称加密公钥加密,私钥解密,避免密钥传输
交易数据/个人信息AES对称加密性能好,适合大量数据加密
请求防篡改HMAC-SHA256签名验证数据完整性
防重放攻击时间戳+随机数确保请求唯一性

三、通用缓存:性能优化与数据管理

缓存设计需平衡速度、空间和一致性,金融APP尤其需注意数据实时性和安全性。

🗂️ 三级缓存架构

// 1. 内存缓存(NSCache - 自动清理)
class MemoryCache {
    static let shared = MemoryCache()
    private let cache = NSCache<NSString, AnyObject>()
    
    private init() {
        cache.countLimit = 100 // 限制缓存数量
        cache.totalCostLimit = 50 * 1024 * 1024 // 50MB内存限制
    }
    
    func cacheTransaction(_ transaction: Transaction, for key: String) {
        cache.setObject(transaction, forKey: key as NSString)
    }
}

// 2. 磁盘缓存(FileManager - 持久化)
class DiskCache {
    func cacheData(_ data: Data, for key: String, expiration: TimeInterval = 3600) {
        let fileURL = cacheDirectory.appendingPathComponent(key.md5)
        
        // 存储缓存元数据(包含过期时间)
        let metadata = CacheMetadata(expiration: Date().addingTimeInterval(expiration))
        let metadataData = try? JSONEncoder().encode(metadata)
        
        // 写入文件
        try? data.write(to: fileURL)
        try? metadataData?.write(to: metadataURL(for: key))
    }
}

// 3. 数据库缓存(Core Data - 结构化数据)
class DatabaseCache {
    func cacheUserTransactions(_ transactions: [Transaction]) {
        let context = persistentContainer.viewContext
        
        // 批量插入/更新
        transactions.forEach { transaction in
            let cdTransaction = CDTransaction(context: context)
            cdTransaction.id = transaction.id
            cdTransaction.amount = transaction.amount
            cdTransaction.timestamp = transaction.date
        }
        
        try? context.save()
    }
}

⚙️ 缓存策略实现

1. 缓存键设计

struct CacheKey {
    // 复合键:类型 + 业务ID + 版本号
    static func forTransactionList(userId: String, page: Int, pageSize: Int) -> String {
        return "transaction_list_\(userId)_\(page)_\(pageSize)_v2"
    }
    
    // 带版本的键:数据结构变更时自动失效旧缓存
    static func forUserProfile(userId: String) -> String {
        return "user_profile_\(userId)_v3" // 版本号递增
    }
}

2. 缓存清理策略

// LRU(最近最少使用)清理
class LRUCacheManager {
    private var accessHistory: [String: Date] = [:]
    
    func cleanupIfNeeded() {
        let now = Date()
        let expirationInterval: TimeInterval = 7 * 24 * 3600 // 一周
        
        for (key, lastAccess) in accessHistory {
            if now.timeIntervalSince(lastAccess) > expirationInterval {
                removeCache(for: key)
                accessHistory.removeValue(forKey: key)
            }
        }
    }
}

// 基于容量的清理
class CapacityBasedCache {
    private let maxDiskSize: Int = 100 * 1024 * 1024 // 100MB
    
    func cleanupOldFiles() {
        var totalSize = 0
        let files = sortedCacheFilesByAccessDate() // 按访问时间排序
        
        for file in files {
            totalSize += file.size
            if totalSize > maxDiskSize {
                try? FileManager.default.removeItem(at: file.url)
            }
        }
    }
}

3. 数据缓存特别处理

class FinancialDataCache {
    // 实时性要求高的数据:短时间缓存
    func cacheAccountBalance(_ balance: Double, for userId: String) {
        // 余额数据只缓存5分钟
        cache.setObject(balance, forKey: "balance_\(userId)", expiresIn: 300)
    }
    
    // 交易记录:可适当延长缓存时间
    func cacheTransactions(_ transactions: [Transaction], for userId: String) {
        // 交易记录缓存1小时
        cache.setObject(transactions, forKey: "transactions_\(userId)", expiresIn: 3600)
    }
    
    // 敏感数据:退出即清理
    func clearSensitiveData() {
        cache.removeObject(forKey: "balance_\(userId)")
        cache.removeObject(forKey: "recent_transfers_\(userId)")
        // 同时清理磁盘缓存
        DiskCache.clearUserData(userId)
    }
}

📈 缓存性能监控

class CacheMetrics {
    var hitCount = 0
    var missCount = 0
    var totalSize: Int64 = 0
    
    var hitRate: Double {
        return Double(hitCount) / Double(hitCount + missCount)
    }
    
    func logCacheHit() {
        hitCount += 1
        if hitRate < 0.6 {
            // 命中率过低,考虑调整缓存策略
            print("警告:缓存命中率偏低 \(hitRate)")
        }
    }
}

🎯 要点总结

  1. 内存泄露:强调工具使用(Memory Graph)和常见场景(闭包、代理),结合金融APP谈敏感数据清理
  2. 网络加密:说明分层理念(HTTPS打底+业务层增强),重点阐述参数签名防篡改和密钥安全管理
  3. 通用缓存:解释三级架构(内存→磁盘→DB),强调金融数据的特殊缓存策略(短时间、及时清理)

三者关联性:良好的缓存设计减少不必要的网络请求(降低加密传输压力),正确的内存管理确保缓存对象及时释放,三者共同保障APP性能与安全。