「OC」GCD的相关练习
文章目录
- 「OC」GCD的相关练习
- 前言
- 问题一:使用 GCD 实现并发任务限制
- 我写的代码:
- GPT做出的优化:
- 问题2:任务完成后的顺序执行
- 我写的代码:
- GPT给出的优化:
- 问题三:定时器与 GCD
- 我写的代码
- 问题四:异步加载多个图片并更新 UI
- GPT给出的优化:
- 问题五:多个任务的优先级控制
- 我写的代码:
- GPT给出的优化:
- 6. **问题:延迟任务执行**
- 我写的代码:
- 问题七:并行执行多个下载任务,并等待所有任务完成后进行操作
- 我写的代码:
- GPT给出的优化:
- 问题八:队列中任务的延迟执行
- 我写的代码:
- GPT给出的优化:
- 问题九:在后台线程中同时执行多个任务并在主线程中汇总结果
- 我写的代码:
- 问题十:使用 GCD 实现懒加载
- 我写的代码:
- 总结
前言
为了掌握GCD的相关用法,我让GPT给我出了十道与GCD操作相关的题目,来巩固我对GCD的用法
问题一:使用 GCD 实现并发任务限制
问题描述: 你需要在应用中同时进行多个耗时的任务(如网络请求、文件下载等),但为了避免线程过多导致性能问题,你希望限制同时运行的任务数。具体要求如下:
- 最大并发数为 5。
- 当超过 5 个任务时,其他任务应该等待,直到有任务完成后才能继续执行。
要求: 使用 GCD 的 信号量(dispatch_semaphore
)来限制最大并发数。
我写的代码:
dispatch_semaphore_t sem = dispatch_semaphore_create(5);for (int i = 0 ; i < 20; i++) {dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);NSLog(@"%@",[NSThread currentThread]);[NSThread sleepForTimeInterval:2];dispatch_semaphore_signal(sem);});}[NSThread sleepForTimeInterval:41];
在这里为了防止主线程提前结束,我在主线程之中简单的做了[NSThread sleepForTimeInterval:41];
来防止主线程提前结束,无法进行异步打印
GPT做出的优化:
dispatch_semaphore_t sem = dispatch_semaphore_create(5);
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.text.GCDTest", DISPATCH_QUEUE_CONCURRENT);
for (int i = 0 ; i < 20; i++) {dispatch_group_async(group, queue, ^{dispatch_semaphore_wait(sem, DISPATCH_TIME_FOREVER);NSLog(@"第 %d 个任务 正在线程 %@ 执行", i, [NSThread currentThread]);[NSThread sleepForTimeInterval:2];dispatch_semaphore_signal(sem);});}dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"已完成所有任务");
可以看到,GPT使用了一个group来进行完成等待机制,只有当这个组内的任务全部执行完毕,才会结束主线程,这就比我粗暴的休眠主线程更加便利。
问题2:任务完成后的顺序执行
问题描述: 你需要在后台异步执行三个任务,确保任务按顺序完成,并且每个任务的执行都依赖前一个任务的完成结果。例如:
- 第一个任务:下载数据。
- 第二个任务:处理数据。
- 第三个任务:展示数据。
要求:
- 使用 GCD 来确保任务按照顺序执行。
- 不要在主线程中阻塞其他任务。
提示: 可以考虑使用 dispatch_group
来管理多个任务并保证顺序执行。
我写的代码:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.text.GCDTest", DISPATCH_QUEUE_SERIAL);dispatch_group_async(group, queue, ^{NSLog(@"下载数据");[NSThread sleepForTimeInterval:1];});dispatch_group_async(group, queue, ^{NSLog(@"解析数据");[NSThread sleepForTimeInterval:1];
});dispatch_group_async(group, queue, ^{NSLog(@"展示数据");[NSThread sleepForTimeInterval:1];
});dispatch_group_notify(group, queue, ^{NSLog(@"任务完成");[NSThread sleepForTimeInterval:1];
});
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
我直接使用一个异步串行队列,对相应任务进行操作,这样保证顺序是一定的。
GPT给出的优化:
没什么区别,直接贴出来
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.text.GCDTest", DISPATCH_QUEUE_SERIAL);dispatch_group_async(group, queue, ^{NSLog(@"下载数据");[NSThread sleepForTimeInterval:1]; // 模拟耗时操作NSLog(@"下载完成");
});dispatch_group_async(group, queue, ^{NSLog(@"解析数据");[NSThread sleepForTimeInterval:1];NSLog(@"解析完成");
});dispatch_group_async(group, queue, ^{NSLog(@"展示数据");[NSThread sleepForTimeInterval:1];NSLog(@"展示完成");
});dispatch_group_notify(group, queue, ^{NSLog(@"任务完成");
});// 如果不需要阻塞主线程,这部分代码可以省略
dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"所有任务都完成,主线程继续");
问题三:定时器与 GCD
问题描述: 你需要实现一个定时器功能,要求每隔 2 秒执行一次任务,直到任务执行 5 次。执行完后,停止定时器。
要求:
- 使用
dispatch_after
或dispatch_source
来实现定时任务。 - 确保每次任务执行间隔为 2 秒。
- 完成 5 次任务后停止定时器。
我写的代码
使用dispatch_source_t
__block int count = 0;
dispatch_queue_t queue = dispatch_queue_create("com.text.GCDTest", DISPATCH_QUEUE_CONCURRENT);
dispatch_source_t timer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, queue);
dispatch_source_set_timer(timer, DISPATCH_TIME_NOW, 2 * NSEC_PER_SEC, 0 * NSEC_PER_SEC);
dispatch_source_set_event_handler(timer, ^{NSLog(@"执行任务 %d",count);count++;if (count == 5) {dispatch_source_cancel(timer);}
});// 设置定时器取消时的清理任务
dispatch_source_set_cancel_handler(timer, ^{NSLog(@"定时器已取消,清理资源");
});
dispatch_activate(timer);
dispatch_main();
GPT给出的方法:
GPT使用dispatch_after
,用递归进行类似功能的完成
__block int count = 0; // 定义计数器
dispatch_queue_t queue = dispatch_queue_create("com.text.GCDTest", DISPATCH_QUEUE_CONCURRENT);// 定义一个递归任务
void (^recursiveTask)(void) = ^{if (count < 5) {NSLog(@"执行任务 %d", count);count++;// 递归调用,延迟2秒后执行dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), queue, recursiveTask);} else {NSLog(@"任务完成");}
};// 开始任务
recursiveTask();// 保持主线程运行
[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:15]];
使用了一个递归调用,其中[[NSRunLoop mainRunLoop] runUntilDate:[NSDate dateWithTimeIntervalSinceNow:15]];
,用于保证主线程继续运行,能够正常处理各种可能到来的事件。
问题四:异步加载多个图片并更新 UI
问题描述: 你需要从网络异步加载 10 张图片,并在加载完成后更新 UI(显示在一个图片画廊中)。要求:
- 使用
dispatch_async
来加载每张图片。 - 确保 UI 更新发生在主线程。
- 加载完成后,显示一个提示“图片加载完成”。
提示: 可以使用 dispatch_group
来等待所有图片加载完成。
NSMutableArray *ImageArr = [NSMutableArray array];for (int i = 0; i < 10; i++) {NSString *imageUrlString = [NSString stringWithFormat:@"https://example.com/image-%d.jpg", i];NSURL *imageUrl = [NSURL URLWithString:imageUrlString];dispatch_group_enter(group);AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];[manager GET:imageUrl.absoluteString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {UIImage *image = [UIImage imageWithData:responseObject];if (image) {[ImageArr addObject:image];}dispatch_group_leave(group);} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {NSLog(@"图片加载失败: %@", error.localizedDescription);dispatch_group_leave(group);}];}dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"下载完成");});}
这里我使用AFN第三方库去模拟这个下载图片的过程,这些获取的图片都是按照下载完成顺序的。
GPT给出的优化:
在原先的代码,使用 NSMutableArray
在多个线程中进行修改,而 NSMutableArray
本身不是线程安全的。需要确保在访问时进行线程同步。另外这样下载数据并不能保证图片数据会根据图片原先URL的位置去存储,因为下载结束的时间不同。每次循环都创建新的 AFHTTPSessionManager
实例。
NSMutableArray *ImageArr = [NSMutableArray arrayWithCapacity:10]; // 假设有10张图片
dispatch_group_t group = dispatch_group_create();for (int i = 0; i < 10; i++) {NSString *imageUrlString = [NSString stringWithFormat:@"https://example.com/image-%d.jpg", i];NSURL *imageUrl = [NSURL URLWithString:imageUrlString];dispatch_group_enter(group);AFHTTPSessionManager *manager = [AFHTTPSessionManager manager];[manager GET:imageUrl.absoluteString parameters:nil progress:nil success:^(NSURLSessionDataTask * _Nonnull task, id _Nullable responseObject) {UIImage *image = [UIImage imageWithData:responseObject];if (image) {// 保证按索引顺序存储图片@synchronized (ImageArr) {[ImageArr replaceObjectAtIndex:i withObject:image];}}dispatch_group_leave(group); // 完成任务} failure:^(NSURLSessionDataTask * _Nullable task, NSError * _Nonnull error) {NSLog(@"图片加载失败: %@", error.localizedDescription);dispatch_group_leave(group);}];
}// 等待所有任务完成后进行操作
dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"下载完成,下载的图片数:%lu", (unsigned long)ImageArr.count);// 这里可以更新UI,例如显示图片
});
为了确保图片下载的顺序与任务的发起顺序一致,为每个下载任务分配一个索引,确保图片按照该索引顺序被存储。使用一个数组,保存图片的下载顺序。每次下载成功后,将图片放入正确的位置。
问题五:多个任务的优先级控制
问题描述: 你需要执行一系列任务,其中:
- 一些任务优先级较高,需要尽快完成。
- 其他任务可以延迟执行。 要求:
- 使用
dispatch_queue_t
配合 优先级 来实现任务的优先级控制。 - 高优先级任务要尽可能先执行,低优先级任务可以等待高优先级任务完成后执行。
我写的代码:
void highPriorityTask(int i) {NSLog(@"高优先级任务 %d 开始", i);[NSThread sleepForTimeInterval:1]; // 模拟任务耗时NSLog(@"高优先级任务 %d 结束", i);
}void lowPriorityTask(int i) {NSLog(@"低优先级任务 %d 开始", i);[NSThread sleepForTimeInterval:1]; // 模拟任务耗时NSLog(@"低优先级任务 %d 结束", i);
}int main(int argc, const char * argv[]) {@autoreleasepool {dispatch_queue_t highPrioityQueue = dispatch_queue_create("com.text.highProrityQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_set_target_queue(highPrioityQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0));dispatch_queue_t lowPriorityQueue = dispatch_queue_create("com.text.lowProrityQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_set_target_queue(lowPriorityQueue, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0));for (int i = 0; i < 3; i++) {dispatch_async(highPrioityQueue, ^{highPriorityTask(i);});}// 提交低优先级任务到低优先级队列for (int i = 0; i < 3; i++) {dispatch_async(lowPriorityQueue, ^{lowPriorityTask(i);});}[NSThread sleepForTimeInterval:10];}return 0;
}
这里我使用了dispatch_set_target_queue
,让自定义队列的队列目标分别设置为高优先级和低优先级的队列,得到以下结果
可以看到其实对于高优先级,由于 dispatch_set_target_queue
仅将目标队列的优先级调整了,但如果高优先级任务尚未开始,低优先级的任务也可能被调度。也就是说,对于不同优先级的任务,并不会在任务调度的中途进行抢占。换句话说,GCD 不会中断正在运行的低优先级任务来立即执行高优先级任务。
GPT给出的优化:
为了保证在高优先级的任务先完成,而不先启动低优先级的任务,GPT给我加了一个栅栏函数,保证在高优先级的任务先完成,再完成低优先级的任务
dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);// 提交高优先级任务到并发队列for (int i = 0; i < 3; i++) {dispatch_async(concurrentQueue, ^{highPriorityTask(i);});}// 使用 dispatch_barrier_async 确保前面的高优先级任务全部完成dispatch_barrier_async(concurrentQueue, ^{NSLog(@"=== 所有高优先级任务完成,开始执行低优先级任务 ===");});// 提交低优先级任务到并发队列for (int i = 0; i < 3; i++) {dispatch_async(concurrentQueue, ^{lowPriorityTask(i);});}dispatch_main();
6. 问题:延迟任务执行
问题描述: 你需要在一定延迟后执行一个操作,比如用户点击按钮后 3 秒钟才执行某个任务。要求:
- 使用
dispatch_after
来延迟任务的执行。 - 打印“延迟执行的任务已开始”。
提示: 考虑在主线程中触发延迟任务。
我写的代码:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_group_enter(group);
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{NSLog(@"%@",[NSThread currentThread]);[NSThread sleepForTimeInterval:1];dispatch_group_leave(group);});dispatch_group_wait(group, DISPATCH_TIME_FOREVER);
NSLog(@"任务完成");
这个代码和GPT的给出的大致一致,就不贴出来了
问题七:并行执行多个下载任务,并等待所有任务完成后进行操作
问题描述: 你需要并行下载多个文件,并在所有文件下载完成后更新 UI。具体要求:
- 假设你有一个包含多个文件 URL 的数组。
- 对每个文件 URL 进行异步下载。
- 当所有文件下载完成后,更新 UI,显示“所有文件已下载”。
提示: 可以使用 dispatch_group
来监听所有异步任务的完成。
我写的代码:
NSArray* fileUrls = @[@"https://example.com/file1.txt", @"https://example.com/file2.txt", @"https://example.com/file3.txt"];NSMutableArray* downloadedFiles = [NSMutableArray array];
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.text.GCDTest", DISPATCH_QUEUE_CONCURRENT);for (NSString *str in fileUrls) {dispatch_group_enter(group);NSURL *url = [NSURL URLWithString:str];dispatch_async(queue, ^{NSData *data = [NSData dataWithContentsOfURL:url];@synchronized (downloadedFiles) {[downloadedFiles addObject:data];dispatch_group_leave(group);}});}dispatch_group_notify(group, queue, ^{NSLog(@"所有任务完成");});
在以上代码,我使用了@synchronized来保证这个结果数组的线程安全。
GPT给出的优化:
在先前的程序之中,我并没有对网络申请的data进行判空,导致获得的数据可能为空。而且使用@synchronized会导致线程开销较大,GPT此处给出了用栅栏函数,dispatch_barrier_async 会在所有并行任务完成后,独占执行添加数据的操作,这比 @synchronized
更高效。
NSArray* fileUrls = @[@"https://example.com/file1.txt",@"https://example.com/file2.txt",@"https://example.com/file3.txt"];NSMutableArray* downloadedFiles = [NSMutableArray array];
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_queue_create("com.text.GCDTest", DISPATCH_QUEUE_CONCURRENT);// 使用并发队列控制对 downloadedFiles 的操作
dispatch_queue_t fileAccessQueue = dispatch_queue_create("com.example.fileAccessQueue", DISPATCH_QUEUE_CONCURRENT);for (NSString *str in fileUrls) {dispatch_group_enter(group);NSURL *url = [NSURL URLWithString:str];dispatch_async(queue, ^{NSData *data = [NSData dataWithContentsOfURL:url];if (data) {dispatch_barrier_async(fileAccessQueue, ^{[downloadedFiles addObject:data];});} else {NSLog(@"下载失败,URL: %@", str);}dispatch_group_leave(group);});
}dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"所有任务完成");NSLog(@"下载的文件数量: %lu", (unsigned long)downloadedFiles.count);
});dispatch_main();
问题八:队列中任务的延迟执行
问题描述: 你需要在队列中添加一些任务,并且要求:
- 第一个任务立即执行。
- 第二个任务在 2 秒后执行。
- 第三个任务在 5 秒后执行。
要求:
- 使用
dispatch_after
来安排任务的延迟执行。
我写的代码:
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_async(queue, ^{NSLog(@"任务1正在线程 %@ 执行", [NSThread currentThread]);[NSThread sleepForTimeInterval:2];});dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), queue, ^{NSLog(@"任务2正在线程 %@ 执行", [NSThread currentThread]);[NSThread sleepForTimeInterval:2];});dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(5 * NSEC_PER_SEC)), queue, ^{NSLog(@"任务3正在线程 %@ 执行", [NSThread currentThread]);[NSThread sleepForTimeInterval:2];});dispatch_main();
GPT给出的优化:
GPT一共给出两种优化,一种是使用嵌套进行组合控制依赖,即将下一个任务用dispatch_after
写在任务1之中
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);// 任务1
dispatch_async(queue, ^{NSLog(@"任务1正在线程 %@ 执行", [NSThread currentThread]);[NSThread sleepForTimeInterval:2];// 任务2 延迟 2 秒后执行dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), queue, ^{NSLog(@"任务2正在线程 %@ 执行", [NSThread currentThread]);// 任务3 延迟 3 秒后执行dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(3 * NSEC_PER_SEC)), queue, ^{NSLog(@"任务3正在线程 %@ 执行", [NSThread currentThread]);});});
});// 保持主线程不退出
dispatch_main();
另一种是使用dispatch_block_t
进行依赖操作
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);dispatch_block_t task1 = dispatch_block_create(0, ^{NSLog(@"任务1正在线程 %@ 执行", [NSThread currentThread]);[NSThread sleepForTimeInterval:2];});dispatch_block_t task2 = dispatch_block_create(0, ^{NSLog(@"任务2正在线程 %@ 执行", [NSThread currentThread]);[NSThread sleepForTimeInterval:2];});dispatch_block_t task3 = dispatch_block_create(0, ^{NSLog(@"任务3正在线程 %@ 执行", [NSThread currentThread]);[NSThread sleepForTimeInterval:2];});// 依赖任务dispatch_async(queue, task1);dispatch_block_notify(task1, queue, task2);dispatch_block_notify(task2, queue, task3);dispatch_main();
问题九:在后台线程中同时执行多个任务并在主线程中汇总结果
问题描述: 你需要在后台线程中执行多个耗时的计算任务(如下载多个文件或处理数据),然后将结果汇总并更新 UI。要求:
- 在后台线程中并行执行多个任务。
- 等所有任务完成后,汇总结果并在主线程中更新 UI。
我写的代码:
NSArray *tasks = @[@"task1",@"task2",@"task3",@"task4"];dispatch_queue_t concurrentQueue = dispatch_queue_create("com.example.concurrentQueue", DISPATCH_QUEUE_CONCURRENT);dispatch_group_t group = dispatch_group_create();NSMutableArray *results = [NSMutableArray array];for(NSString *task in tasks) {dispatch_group_enter(group);dispatch_async(concurrentQueue, ^{NSLog(@"开始执行 %@", task);[NSThread sleepForTimeInterval:1];NSString *result = [NSString stringWithFormat:@"%@ - 结果", task];@synchronized (results) {[results addObject:result];}});}dispatch_group_notify(group, dispatch_get_main_queue(), ^{NSLog(@"🎉 所有任务完成,结果为: %@", results);});// 保持主线程不退出,等待任务完成[[NSRunLoop currentRunLoop] run];
关于并发操作的添加数组的问题,GPT有时候使用锁,有时用的是栅栏函数,虽说本质上都是锁,但是有什么区别呢,我问了GPT,得到以下答案
对比项 | @synchronized / NSLock (锁) | dispatch_barrier_async (栅栏函数) |
---|---|---|
适用场景 | 频繁的读写交替操作,需要同时锁住读和写 | 需要高频读取,少量写入的场景(读多写少) |
并行读取 | ❌ 读操作也会被阻塞,性能低 | ✅ 读操作不被阻塞,性能高 |
写操作 | ✅ 只有一个线程能写入,其他读/写被阻塞 | ✅ 只阻塞写入操作,不影响并行读取 |
性能 | ⛔️ 低性能,尤其是频繁读操作 | 🚀 高性能,大幅减少阻塞操作 |
实现难度 | 👍 简单,容易理解 | ⚠️ 略微复杂,仅限并发队列中使用 |
可用场景 | 串行任务、非 GCD 任务、锁定一切 | GCD 并发任务,写操作较少的场景 |
问题十:使用 GCD 实现懒加载
问题描述: 你需要实现一个懒加载功能,当需要时才开始加载数据。具体要求:
- 只有在用户滑动到某个位置时才开始加载数据。
- 数据加载完后,更新 UI(例如显示加载的图片)。
提示: 使用 dispatch_async
来延迟加载,并保证 UI 更新在主线程中。
我写的代码:
其实这个功能有点像知乎日报之中的,下拉加载新闻的内容,只有当tableView被下滑到底端才会进行加载,不过这个加载多了个占位符的图片的功能对我有点启发
#import "ViewController.h"@interface ViewController () <UITableViewDelegate, UITableViewDataSource>@property (nonatomic, strong) UITableView *tableView;
@property (nonatomic, strong) NSArray *imageUrls; // 模拟的图片URL数组
@property (nonatomic, strong) NSMutableDictionary *imageCache; // 图片缓存,避免重复加载@end@implementation ViewController- (void)viewDidLoad {[super viewDidLoad];self.imageUrls = @[@"https://via.placeholder.com/150/FF0000",@"https://via.placeholder.com/150/00FF00",@"https://via.placeholder.com/150/0000FF",@"https://via.placeholder.com/150/FFFF00",@"https://via.placeholder.com/150/FF00FF",@"https://via.placeholder.com/150/00FFFF",@"https://via.placeholder.com/150/000000",@"https://via.placeholder.com/150/888888",@"https://via.placeholder.com/150/123456",@"https://via.placeholder.com/150/654321"];self.imageCache = [NSMutableDictionary dictionary]; // 图片缓存self.tableView = [[UITableView alloc] initWithFrame:self.view.bounds];self.tableView.delegate = self;self.tableView.dataSource = self;[self.view addSubview:self.tableView];
}#pragma mark - UITableViewDataSource- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {return self.imageUrls.count;
}- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {static NSString *cellIdentifier = @"cell";UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];if (!cell) {cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:cellIdentifier];cell.imageView.contentMode = UIViewContentModeScaleAspectFit;}// 显示占位符cell.imageView.image = [UIImage imageNamed:@"placeholder.png"];// 获取当前的图片 URLNSString *imageUrl = self.imageUrls[indexPath.row];// 如果缓存中有图片,直接从缓存加载if (self.imageCache[imageUrl]) {NSLog(@"✅ 从缓存中加载图片: %@", imageUrl);cell.imageView.image = self.imageCache[imageUrl];} else {// 只有当 Cell 可见时,才开始加载图片[self lazyLoadImageForCell:cell atIndexPath:indexPath];}return cell;
}#pragma mark - 滑动结束后触发懒加载- (void)lazyLoadImageForCell:(UITableViewCell *)cell atIndexPath:(NSIndexPath *)indexPath {NSString *imageUrl = self.imageUrls[indexPath.row];// 异步加载图片,防止阻塞主线程dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{NSLog(@"🚀 开始加载图片: %@", imageUrl);NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageUrl]];if (imageData) {UIImage *image = [UIImage imageWithData:imageData];// 加入缓存,避免重复加载@synchronized (self) {self.imageCache[imageUrl] = image;}// 回到主线程更新 UIdispatch_async(dispatch_get_main_queue(), ^{UITableViewCell *updateCell = [self.tableView cellForRowAtIndexPath:indexPath];if (updateCell) {updateCell.imageView.image = image;[updateCell setNeedsLayout];}});} else {NSLog(@"❌ 图片加载失败: %@", imageUrl);}});
}@end
总结
GCD的使用确实是多线程的一个难点,通过完成以上的任务,我更深刻的掌握了GCD的相关用法,希望能对寒假的项目产生帮助