iOS Objective-C应用优化:减少冗余API调用并本地存储数据的设计方案
优化iOS Objective-C应用的JSON数据缓存与复用方案
看起来你已经把基础功能跑通了,但想优化缓存策略、减少重复API调用——这个需求非常合理,毕竟接口返回的是固定168个对象的数组,完全没必要每次切换UI控件都去请求接口。下面是我整理的几个实用优化方案,都是Objective-C开发里的常用实践:
一、核心优化思路
一次性请求并缓存全量168条数据到本地,后续所有UI控件的展示需求都直接从本地读取对应片段,彻底避免重复API请求。
二、本地存储方案选择(针对你的数据规模)
根据168条结构化数据的特点,推荐两种轻量高效的存储方案,你可以根据后续需求选择:
1. NSUserDefaults(快速上手,适合小体量数据)
优点:代码极简,无需额外依赖,完全能hold住168条数据的存储需求。
缺点:如果未来数据量大幅增长,可能会有性能问题,但当前场景下完全够用。
代码示例:
存储全量数据(请求成功后执行)
// 假设你已经通过GET请求拿到了NSArray *responseArray NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; // 你的数组元素都是NSNumber,直接存就行 [defaults setObject:responseArray forKey:@"CachedHourlyDataset"]; // 给缓存加个过期时间(比如24小时后重新请求,可按需调整) [defaults setObject:[NSDate date] forKey:@"DatasetCacheTimestamp"]; [defaults synchronize];
读取缓存并判断是否有效
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSArray *cachedData = [defaults objectForKey:@"CachedHourlyDataset"]; NSDate *cacheTime = [defaults objectForKey:@"DatasetCacheTimestamp"]; // 检查缓存是否存在且未过期(这里设为24小时有效期) NSTimeInterval timeElapsed = [[NSDate date] timeIntervalSinceDate:cacheTime]; BOOL isCacheValid = cachedData != nil && timeElapsed < 24*60*60; if (isCacheValid) { // 直接取对应片段给Button1展示(注意Objective-C数组索引从0开始,obj1对应index0) NSRange targetRange = NSMakeRange(0, 10); // obj1到obj10 NSArray *button1Data = [cachedData subarrayWithRange:targetRange]; // 把数据传给UI控件渲染 [self renderButton1Data:button1Data]; } else { // 缓存失效,发起新请求,请求成功后再存入本地 [self fetchLatestDataAndCache]; }
2. Core Data(适合未来扩展场景)
如果后续你需要更复杂的数据查询(比如按day或hour筛选),或者数据规模可能变大,Core Data会更灵活。你可以创建一个HourlyData实体,包含value、day、hour三个属性,把168条数据存入Core Data,后续直接查询对应范围的数据即可。
简单查询示例:
// 获取前10条数据(对应obj1-obj10) NSFetchRequest *fetchRequest = [NSFetchRequest fetchRequestWithEntityName:@"HourlyData"]; fetchRequest.fetchLimit = 10; // 按day和hour排序,保证顺序和接口返回一致 fetchRequest.sortDescriptors = @[ [NSSortDescriptor sortDescriptorWithKey:@"day" ascending:YES], [NSSortDescriptor sortDescriptorWithKey:@"hour" ascending:YES] ]; NSError *error; NSArray *button1Data = [self.managedObjectContext executeFetchRequest:fetchRequest error:&error]; if (!error) { [self renderButton1Data:button1Data]; }
三、封装数据管理单例,统一处理逻辑
为了避免各个UI控件重复写缓存判断、数据切片的代码,建议封装一个单例数据管理类(比如DataManager),把缓存读取、数据请求、切片逻辑都集中在这里:
// DataManager.h @interface DataManager : NSObject + (instancetype)sharedManager; // 获取指定范围的数据 - (NSArray *)dataInRange:(NSRange)range; // 手动刷新数据(比如用户下拉刷新) - (void)refreshDataWithCompletion:(void(^)(BOOL success))completion; @end // DataManager.m @implementation DataManager static DataManager *_sharedInstance; + (instancetype)sharedManager { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ _sharedInstance = [[DataManager alloc] init]; }); return _sharedInstance; } - (NSArray *)dataInRange:(NSRange)range { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; NSArray *cachedData = [defaults objectForKey:@"CachedHourlyDataset"]; NSDate *cacheTime = [defaults objectForKey:@"DatasetCacheTimestamp"]; NSTimeInterval timeElapsed = [[NSDate date] timeIntervalSinceDate:cacheTime]; if (cachedData && timeElapsed < 24*60*60) { // 防止range越界 if (range.location + range.length > cachedData.count) { range.length = cachedData.count - range.location; } return [cachedData subarrayWithRange:range]; } // 缓存失效,后台刷新数据,先返回空 [self refreshDataWithCompletion:nil]; return nil; } - (void)refreshDataWithCompletion:(void(^)(BOOL success))completion { // 用NSURLSession发起GET请求(替换成你的API地址) NSURL *apiURL = [NSURL URLWithString:@"https://your-api-url.com/data"]; NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:apiURL completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) { if (!error && data) { NSError *jsonError; NSArray *responseArray = [NSJSONSerialization JSONObjectWithData:data options:0 error:&jsonError]; // 验证数据完整性(必须是168条) if (responseArray && responseArray.count == 168) { NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults setObject:responseArray forKey:@"CachedHourlyDataset"]; [defaults setObject:[NSDate date] forKey:@"DatasetCacheTimestamp"]; [defaults synchronize]; if (completion) completion(YES); // 发送通知,告诉UI控件数据已更新 [[NSNotificationCenter defaultCenter] postNotificationName:@"HourlyDataUpdated" object:nil]; } else { if (completion) completion(NO); } } else { if (completion) completion(NO); } }]; [task resume]; } @end
然后在你的UI控件里,只需要简单调用这个单例的方法:
// Button1点击事件 - (IBAction)button1Tapped:(UIButton *)sender { NSArray *targetData = [[DataManager sharedManager] dataInRange:NSMakeRange(0, 10)]; if (targetData) { [self renderButton1Data:targetData]; } else { // 显示加载中,等待数据更新通知 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(onDataUpdated:) name:@"HourlyDataUpdated" object:nil]; } } - (void)onDataUpdated:(NSNotification *)note { [[NSNotificationCenter defaultCenter] removeObserver:self name:@"HourlyDataUpdated" object:nil]; NSArray *targetData = [[DataManager sharedManager] dataInRange:NSMakeRange(0, 10)]; [self renderButton1Data:targetData]; }
四、额外优化小技巧
- 数据完整性校验:每次请求新数据后,一定要检查数组长度是否为168,避免脏数据存入本地。
- 可配置缓存有效期:把24小时的硬编码改成可配置的参数,方便后续调整。
- 离线友好:如果缓存过期但没有网络,依然展示旧缓存数据,提升用户体验(可以在
refreshDataWithCompletion里判断网络状态)。
内容的提问来源于stack exchange,提问作者humble_pie




