
💎【行业认证·权威头衔】
✔ 华为云天团核心成员:特约编辑/云享专家/开发者专家/产品云测专家
✔ 开发者社区全满贯:CSDN博客&商业化双料专家/阿里云签约作者/腾讯云内容共创官/掘金&亚马逊&51CTO顶级博主
✔ 技术生态共建先锋:横跨鸿蒙、云计算、AI等前沿领域的技术布道者
🏆【荣誉殿堂】
🎖 连续三年蝉联"华为云十佳博主"(2022-2024)
🎖 双冠加冕CSDN"年度博客之星TOP2"(2022&2023)
🎖 十余个技术社区年度杰出贡献奖得主
📚【知识宝库】
覆盖全栈技术矩阵:
◾ 编程语言:.NET/Java/Python/Go/Node...
◾ 移动生态:HarmonyOS/iOS/Android/小程序
◾ 前沿领域:物联网/网络安全/大数据/AI/元宇宙
◾ 游戏开发:Unity3D引擎深度解析
文章目录
- 🚀前言
- 🚀一、数据存储与本地缓存
-
- [🔎6.4.1 CoreData 与 SQLite 存储](#🔎6.4.1 CoreData 与 SQLite 存储)
- [🔎6.4.2 文件管理与 UserDefaults](#🔎6.4.2 文件管理与 UserDefaults)
- [🔎6.4.3 内存缓存与NSCache](#🔎6.4.3 内存缓存与NSCache)
🚀前言
本章深入探讨了iOS端应用开发的关键技术与实现方法,重点介绍了如何将DeepSeek 的强大AI能力集成到iOS应用中。通过详细的步骤和代码示例,本章阐明了如何在iOS平台上配置和使用DeepSeekSDK,如何与后端进行高效的数据交互,以及如何处理多种AI任务。结合iOS平台的开发特点,优化了性能、网络请求和用户体验,使得AI应用能够在iOS设备上高效、稳定地运行。
🚀一、数据存储与本地缓存
本节详细阐述了iOS中常用的存储方式,如UserDefaults、CoreData、SQLite以及文件系统操作。还特别讨论了如何利用本地缓存技术提升应用性能,减少不必要的网络请求,并优化用户体验。通过对数据持久化的深度解析,本节为开发者提供了在iOS平台上高效存储和缓存数据的实用方法。
🔎6.4.1 CoreData 与 SQLite 存储
在现代移动应用开发中,持久化存储是一个关键功能,它允许应用在关闭或重启后保存和恢复数据。iOS提供了几种存储数据的方式,其中CoreData和SQLite是两种非常常见的选择。它们各自有其特点和应用场景,但都能够提供高效、持久的存储解决方案。CoreData是苹果提供的一个框架,它允许开发者以对象的形式操作数据库,并通过对象模型来管理数据。CoreData不仅可以轻松地处理对象间的关系,还提供了内存管理、对象版本控制、以及与数据库的无缝集成。CoreData通过内部封装了SQLite来提供持久化存储,因此可以通过CoreData处理SQLite数据库的操作,但提供了更高层次的抽象。
SQLite是一个轻量级的关系型数据库,它是跨平台的,并且是应用中常用的数据存储方式。SQLite为iOS开发者提供了直接访问数据库文件的能力,通过SQL查询语言来操作数据。相比CoreData,SQLite提供了更细粒度的控制,但需要更多的手动管理工作。
在本小节中,我们将结合使用CoreData和SQLite来展示如何在iOS中实现数据存储功能,尤其是在需要高效存储与检索的AI应用中,如何利用这两种存储方式来存储用户数据、历史记录以及DeepSeek的API返回的数据。
【例6-8】 展示如何在iOS应用中结合使用CoreData与SQLite来存储从DeepSeek API返回的数据。我们通过CoreData进行数据持久化,同时使用SQLite作为底层存储。该示例包括以下几个步骤:
- 定义数据模型。
- 创建CoreData模型并配置SQLite存储。
- 通过DeepSeek API获取数据并保存到数据库中。
swift
import UIKit
import CoreData
// 定义数据模型Post
struct Post: Codable {
let userId: Int
let id: Int
let title: String
let body: String
}
class ViewController: UIViewController {
var dataLabel: UILabel!
var fetchButton: UIButton!
var managedContext: NSManagedObjectContext!
override func viewDidLoad() {
super.viewDidLoad()
// 设置UI元素
setupUI()
// 设置CoreData上下文
guard let appDelegate = UIApplication.shared.delegate as? AppDelegate else {
return
}
managedContext = appDelegate.persistentContainer.viewContext
}
func setupUI() {
// 创建UILabel以显示存储数据
dataLabel = UILabel()
dataLabel.frame = CGRect(x: 20, y: 100, width: 300, height: 100)
dataLabel.text = "Data will be shown here."
dataLabel.numberOfLines = 0
dataLabel.textAlignment = .center
view.addSubview(dataLabel)
// 创建UIButton以触发网络请求
fetchButton = UIButton(type: .system)
fetchButton.frame = CGRect(x: 20, y: 250, width: 300, height: 40)
fetchButton.setTitle("Fetch Data", for: .normal)
fetchButton.addTarget(self, action: #selector(fetchData), for: .touchUpInside)
view.addSubview(fetchButton)
}
@objc func fetchData() {
let urlString = "https://jsonplaceholder.typicode.com/posts/1"
guard let url = URL(string: urlString) else {
print("Invalid URL")
return
}
// 发起请求
sendRequest(with: url)
}
func sendRequest(with url: URL) {
let session = URLSession.shared
let task = session.dataTask(with: url) { data, response, error in
if let error = error {
print("Network error: \(error.localizedDescription)")
return
}
// 确保响应成功
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
print("Failed to receive valid response")
return
}
// 解析JSON数据
if let data = data {
do {
let post = try JSONDecoder().decode(Post.self, from: data)
self.savePostToCoreData(post: post)
DispatchQueue.main.async {
self.fetchDataFromCoreData()
}
} catch {
print("Failed to decode JSON: \(error)")
}
}
}
task.resume() // 执行请求
}
// 将API返回的数据保存到CoreData
func savePostToCoreData(post: Post) {
let entity = NSEntityDescription.entity(forEntityName: "PostEntity", in: managedContext)!
let postObject = NSManagedObject(entity: entity, insertInto: managedContext)
postObject.setValue(post.userId, forKey: "userId")
postObject.setValue(post.id, forKey: "id")
postObject.setValue(post.title, forKey: "title")
postObject.setValue(post.body, forKey: "body")
do {
try managedContext.save()
print("Data saved successfully.")
} catch let error as NSError {
print("Could not save. \(error), \(error.userInfo)")
}
}
// 从CoreData读取数据并更新UI
func fetchDataFromCoreData() {
let fetchRequest = NSFetchRequest<NSManagedObject>(entityName: "PostEntity")
do {
let posts = try managedContext.fetch(fetchRequest)
if let post = posts.first {
let title = post.value(forKey: "title") as? String ?? "No title"
let body = post.value(forKey: "body") as? String ?? "No body"
dataLabel.text = "Title: \(title) \nBody: \(body)"
}
} catch let error as NSError {
print("Could not fetch data. \(error), \(error.userInfo)")
}
}
}
// AppDelegate配置CoreData
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
lazy var persistentContainer: NSPersistentContainer = {
let container = NSPersistentContainer(name: "PostModel")
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
if let error = error as NSError? {
fatalError("Unresolved error \(error), \(error.userInfo)")
}
})
return container
}()
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let viewController = ViewController()
window?.rootViewController = viewController
window?.makeKeyAndVisible()
return true
}
}
代码说明如下:
- CoreData模型 :在
AppDelegate中,我们配置了NSPersistentContainer来管理CoreData。persistentContainer负责加载持久化存储并管理上下文(NSManagedObjectContext),该上下文用于处理数据操作。 - 数据模型Post :
Post模型遵循Codable协议,用于解析API返回的JSON数据。每个Post对象包含userId、id、title和body字段。 - CoreData操作 :
savePostToCoreData(post:)方法用于将API返回的数据保存到CoreData。首先,我们定义了一个PostEntity实体,该实体的属性与Post模型匹配。然后,将从API返回的数据保存到PostEntity的属性中,并通过managedContext.save()保存到数据库。fetchDataFromCoreData()方法用于从CoreData中读取数据并更新UI。我们通过NSFetchRequest获取所有PostEntity对象,并显示第一个对象的标题和内容。 - 网络请求 :
sendRequest(with:)方法使用URLSession发起网络请求。当请求成功返回数据时,使用JSONDecoder将JSON数据解析为Post对象,并将该对象保存到CoreData中。保存数据后,应用会从CoreData中读取并更新UI。
在用户单击 Fetch Data 按钮后,应用将向 jsonplaceholder.typicode.com 发送请求并获取数据。假设请求成功并返回有效数据,控制台输出以下信息:
Data saved successfully.
当数据成功保存到CoreData中,UI中的 UILabel 将显示从API获取的标题和内容,例如:
Title: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
Body: quia et suscipit\nsuscipit...et id est laborum
本节演示了如何结合CoreData和SQLite进行数据存储,在iOS中实现持久化存储。通过将从DeepSeek API获取的数据保存到CoreData,并通过 NSFetchRequest 读取数据,我们可以在应用中实现高效的数据管理。使用CoreData不仅能简化数据库操作,还能提高数据持久化的效率,是实现本地存储和缓存的理想选择。
🔎6.4.2 文件管理与 UserDefaults
在移动应用中,文件管理与用户偏好设置的保存是两个常见的需求。iOS提供了多种方式来存储数据,其中 UserDefaults 和文件管理是最常见的两种选择。UserDefaults 用于存储小量的用户偏好数据,而文件管理则适用于存储更大或者复杂的结构化数据。通过合理使用这两者,可以有效地提高应用的数据存储能力。
UserDefaults 是iOS中用于存储小型、简单数据(如字符串、数字、布尔值等)的容器。通常用于保存应用的设置、用户偏好和轻量级的会话信息等。UserDefaults 的优点在于操作简单,可以直接存取数据,而无须像数据库那样进行复杂的管理。使用 UserDefaults 时,数据将持久化存储在设备上,并且在应用重新启动时可以获取。它并不适用于存储大量数据或复杂的对象(如大型图片、视频文件等)。
当应用需要存储较大数据时,例如图片、文档或音频文件,可以使用文件管理来实现。iOS提供了丰富的API用于管理文件,如通过 FileManager 来创建、读取、写入和删除文件。文件管理提供了灵活的存储方案,可以用于存储应用的配置文件、缓存数据等。
下面结合DeepSeek API的应用,展示如何使用 UserDefaults 和文件管理来处理和存储从DeepSeek API获取的数据。
【例6-9】 展示如何通过 UserDefaults 存储用户的偏好设置,并通过文件管理存储从DeepSeek API获取的数据。我们将实现以下功能:
- 将用户的配置信息存储到
UserDefaults。 - 将API返回的JSON数据保存到应用的沙盒目录中。
- 从沙盒目录读取并展示存储的数据。
swift
import UIKit
// 定义数据模型Post
struct Post: Codable {
let userId: Int
let id: Int
let title: String
let body: String
}
class ViewController: UIViewController {
var dataLabel: UILabel!
var fetchButton: UIButton!
// 用户设置的键
let userDefaultsKey = "UserSettings"
let fileManager = FileManager.default
override func viewDidLoad() {
super.viewDidLoad()
// 设置UI元素
setupUI()
// 从UserDefaults读取用户设置
readUserDefaults()
}
func setupUI() {
// 创建UILabel以显示存储数据
dataLabel = UILabel()
dataLabel.frame = CGRect(x: 20, y: 100, width: 300, height: 100)
dataLabel.text = "Data will be shown here."
dataLabel.numberOfLines = 0
dataLabel.textAlignment = .center
view.addSubview(dataLabel)
// 创建UIButton以触发网络请求
fetchButton = UIButton(type: .system)
fetchButton.frame = CGRect(x: 20, y: 250, width: 300, height: 40)
fetchButton.setTitle("Fetch Data", for: .normal)
fetchButton.addTarget(self, action: #selector(fetchData), for: .touchUpInside)
view.addSubview(fetchButton)
}
@objc func fetchData() {
let urlString = "https://jsonplaceholder.typicode.com/posts/1"
guard let url = URL(string: urlString) else {
print("Invalid URL")
return
}
// 发起请求
sendRequest(with: url)
}
func sendRequest(with url: URL) {
let session = URLSession.shared
let task = session.dataTask(with: url) { data, response, error in
if let error = error {
print("Network error: \(error.localizedDescription)")
return
}
// 确保响应成功
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
print("Failed to receive valid response")
return
}
// 解析JSON数据
if let data = data {
do {
let post = try JSONDecoder().decode(Post.self, from: data)
self.saveDataToFile(post: post)
DispatchQueue.main.async {
self.fetchDataFromFile()
}
} catch {
print("Failed to decode JSON: \(error)")
}
}
}
task.resume() // 执行请求
}
// 将数据保存到文件(沙盒)
func saveDataToFile(post: Post) {
// 获取沙盒路径
guard let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
return
}
let fileURL = documentDirectory.appendingPathComponent("postData.json")
// 将数据转换为JSON并保存到文件
do {
let encoder = JSONEncoder()
let data = try encoder.encode(post)
try data.write(to: fileURL)
print("Data saved to file.")
} catch {
print("Failed to save data to file: \(error)")
}
}
// 从文件中读取数据
func fetchDataFromFile() {
// 获取沙盒路径
guard let documentDirectory = fileManager.urls(for: .documentDirectory, in: .userDomainMask).first else {
return
}
let fileURL = documentDirectory.appendingPathComponent("postData.json")
// 从文件读取数据
do {
let data = try Data(contentsOf: fileURL)
let decoder = JSONDecoder()
let post = try decoder.decode(Post.self, from: data)
dataLabel.text = "Title: \(post.title)\nBody: \(post.body)"
} catch {
print("Failed to read data from file: \(error)")
}
}
// 读取UserDefaults中的设置
func readUserDefaults() {
let defaults = UserDefaults.standard
if let settings = defaults.dictionary(forKey: userDefaultsKey) {
print("User settings retrieved from UserDefaults: \(settings)")
} else {
print("No user settings found in UserDefaults.")
}
}
// 保存用户设置到UserDefaults
func saveUserDefaults() {
let defaults = UserDefaults.standard
let settings: [String: Any] = [
"theme": "Dark",
"notificationsEnabled": true,
"lastLogin": Date()
]
defaults.set(settings, forKey: userDefaultsKey)
print("User settings saved to UserDefaults.")
}
}
// AppDelegate和其他必要的配置
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let viewController = ViewController()
window?.rootViewController = viewController
window?.makeKeyAndVisible()
// 保存用户设置(只需执行一次)
viewController.saveUserDefaults()
return true
}
}
代码说明如下:
- UserDefaults存储用户设置 :
saveUserDefaults()方法将一些基本的用户设置(如主题、通知开启状态、上次登录时间)保存到UserDefaults。这些设置通常用于保存应用的用户偏好配置。通过UserDefaults.standard.set()方法,我们可以将数据以键值对的形式存储。存储的数据可以是String、Int、Bool等类型。 - 保存和读取文件数据 :
saveDataToFile(post:)方法将Post数据模型编码为JSON格式,并将其保存到应用的沙盒目录中的postData.json文件。这里使用了JSONEncoder来编码数据,Data.write(to:)方法用于将数据写入文件。fetchDataFromFile()方法读取存储在文件中的数据。通过Data(contentsOf:)方法读取文件内容,然后使用JSONDecoder将数据解码为Post对象。 - 网络请求与数据保存 :
sendRequest(with:)方法发起GET请求从API获取数据。当数据成功返回后,Post对象被保存到文件中,并通过fetchDataFromFile()从文件中读取数据并更新UI。JSONDecoder用于解码API返回的JSON数据,并通过saveDataToFile(post:)保存到文件中。 - UI更新与错误处理:所有的数据读取、保存和UI更新操作都在主线程中进行,确保UI能正确更新。对于任何可能出现的错误,代码中都进行了适当的错误捕获并打印错误信息。
在模拟器或真实设备上运行时,单击 Fetch Data 按钮后,应用将发送请求并把从API返回的数据保存到文件中。假设请求成功并返回有效数据,控制台输出以下信息:
User settings saved to UserDefaults.
Data saved to file.
UI中的 UILabel 将显示以下内容:
Title: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
Body: quia et suscipit\nsuscipit...et id est laborum
本节展示了如何在iOS中结合使用 UserDefaults 和文件管理来存储应用数据。通过 UserDefaults 存储简单的用户设置,并通过文件管理存储更复杂的数据(如API返回的JSON),我们可以高效地处理不同类型的数据存储需求。在开发中,根据数据的复杂性和大小选择合适的存储方案,可以显著提高应用的性能和可维护性。
🔎6.4.3 内存缓存与NSCache
在移动应用开发中,性能优化是一个重要的考量因素。对于频繁使用的数据,可以通过内存缓存来提高数据的访问速度。内存缓存能够有效地减少对磁盘的访问,从而优化数据加载的效率。iOS提供了多种缓存机制,其中 NSCache 是一个高效的内存缓存解决方案。
NSCache 是苹果提供的一种缓存类,它能够自动管理内存缓存。与 NSDictionary 等字典类不同,NSCache 会根据设备的内存压力自动清理不再使用的缓存对象。因此,它特别适合存储频繁读取但不需要持久化的数据(如API请求返回的数据)。使用 NSCache 时,我们可以将API返回的数据、图片等资源缓存到内存中,避免每次请求都进行网络访问,从而提高应用的响应速度和用户体验。
以下示例展示了如何使用 NSCache 缓存从DeepSeek API获取的数据。通过实现缓存策略,我们避免每次请求都访问网络,从而提高了数据加载的效率。
【例6-10】 结合DeepSeek API开发的实际场景,展示如何使用 NSCache 来缓存从API获取的数据。
swift
import UIKit
// 定义数据模型Post
struct Post: Codable {
let userId: Int
let id: Int
let title: String
let body: String
}
class ViewController: UIViewController {
var dataLabel: UILabel!
var fetchButton: UIButton!
// 创建一个NSCache对象来缓存数据
var dataCache = NSCache<NSString, Post>()
// 缓存键
let cacheKey = "postDataCacheKey"
override func viewDidLoad() {
super.viewDidLoad()
// 设置UI元素
setupUI()
// 检查缓存中是否存在数据
if let cachedPost = dataCache.object(forKey: cacheKey as NSString) {
// 如果缓存中有数据,则直接显示
displayData(post: cachedPost)
} else {
print("No cached data found. Fetching data from API...")
}
}
func setupUI() {
// 创建UILabel以显示存储数据
dataLabel = UILabel()
dataLabel.frame = CGRect(x: 20, y: 100, width: 300, height: 100)
dataLabel.text = "Data will be shown here."
dataLabel.numberOfLines = 0
dataLabel.textAlignment = .center
view.addSubview(dataLabel)
// 创建UIButton以触发网络请求
fetchButton = UIButton(type: .system)
fetchButton.frame = CGRect(x: 20, y: 250, width: 300, height: 40)
fetchButton.setTitle("Fetch Data", for: .normal)
fetchButton.addTarget(self, action: #selector(fetchData), for: .touchUpInside)
view.addSubview(fetchButton)
}
@objc func fetchData() {
let urlString = "https://jsonplaceholder.typicode.com/posts/1"
guard let url = URL(string: urlString) else {
print("Invalid URL")
return
}
// 发起请求
sendRequest(with: url)
}
func sendRequest(with url: URL) {
let session = URLSession.shared
let task = session.dataTask(with: url) { data, response, error in
if let error = error {
print("Network error: \(error.localizedDescription)")
return
}
// 确保响应成功
guard let httpResponse = response as? HTTPURLResponse,
httpResponse.statusCode == 200 else {
print("Failed to receive valid response")
return
}
// 解析JSON数据
if let data = data {
do {
let post = try JSONDecoder().decode(Post.self, from: data)
// 将数据缓存到NSCache中
self.cacheData(post: post)
DispatchQueue.main.async {
self.displayData(post: post)
}
} catch {
print("Failed to decode JSON: \(error)")
}
}
}
task.resume() // 执行请求
}
// 缓存数据到NSCache
func cacheData(post: Post) {
dataCache.setObject(post, forKey: cacheKey as NSString)
print("Data cached successfully.")
}
// 从缓存或网络加载数据并显示
func displayData(post: Post) {
dataLabel.text = "Title: \(post.title)\nBody: \(post.body)"
}
}
// AppDelegate和其他必要的配置
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
window = UIWindow(frame: UIScreen.main.bounds)
let viewController = ViewController()
window?.rootViewController = viewController
window?.makeKeyAndVisible()
return true
}
}
代码说明如下:
- 内存缓存NSCache :
NSCache是一个内存缓存容器,用于存储对象数据。在此示例中,dataCache是一个NSCache对象,用于缓存从DeepSeek API获取的数据。NSCache通过setObject(_:forKey:)方法将数据存入缓存,并通过object(forKey:)方法读取缓存的数据。NSCache会根据内存压力自动清理不常使用的缓存对象,这对于避免内存占用过高非常有效。 - 缓存检查 :在视图加载时,应用会检查
NSCache中是否存在缓存的Post对象。如果缓存中存在数据,则直接显示缓存的数据。否则,应用将发起网络请求来获取数据。 - 网络请求与数据存储 :
sendRequest(with:)方法使用URLSession发起GET请求从API获取数据。当请求成功并返回数据后,应用会解析返回的JSON并将其存储到NSCache中。之后,更新UI以显示数据。 - UI更新:当缓存中有数据时,UI直接显示缓存的数据;如果没有缓存数据,则从网络获取数据并缓存,然后更新UI。
- 缓存命中:如果数据已缓存,每次重新打开应用时,UI将直接显示缓存的数据,避免了重复的网络请求。这样可以显著提高应用的响应速度,特别是在网络状况不佳时。
在模拟器或真实设备上运行时,单击 Fetch Data 按钮后,应用将向 jsonplaceholder.typicode.com 发送请求并缓存从API返回的数据。假设请求成功并返回有效数据,控制台输出以下信息:
Data cached successfully.
UILabel 会显示以下内容:
Title: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
Body: quia et suscipit\nsuscipit...et id est laborum
如果用户重新打开应用,且数据已缓存,控制台将显示:
Data cached successfully.
且UI中的 UILabel 依旧显示缓存的内容:
Title: sunt aut facere repellat provident occaecati excepturi optio reprehenderit
Body: quia et suscipit\nsuscipit...et id est laborum
本小节展示了如何使用 NSCache 进行内存缓存,以优化从DeepSeek API获取的数据的性能。通过 NSCache,应用可以将数据缓存在内存中,避免重复的网络请求,从而提高响应速度。该方法特别适用于需要频繁访问的数据,如API请求返回的数据、图片等。此外,使用 NSCache 可以避免不必要的内存占用,它会根据内存压力自动清理缓存内容。