面试题答案
一键面试1. 利用块进行高效的异步任务调度
- GCD 异步调度基础:
在 GCD 中,
dispatch_async
函数用于将任务提交到指定队列并异步执行。例如,将一个耗时的网络请求任务提交到全局队列:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
// 网络请求代码
NSURL *url = [NSURL URLWithString:@"https://example.com/api"];
NSData *data = [NSData dataWithContentsOfURL:url];
// 处理数据
dispatch_async(dispatch_get_main_queue(), ^{
// 更新 UI 等操作
});
});
- 队列优先级与任务控制:
GCD 提供不同优先级的队列,如
DISPATCH_QUEUE_PRIORITY_HIGH
、DISPATCH_QUEUE_PRIORITY_DEFAULT
、DISPATCH_QUEUE_PRIORITY_LOW
和DISPATCH_QUEUE_PRIORITY_BACKGROUND
。可以根据任务的重要性和对性能的影响来选择合适的队列。例如,对于一些不太紧急的后台数据处理任务,可以使用低优先级队列:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{
// 后台数据处理
});
- 组(Group)的使用:
当需要等待一组异步任务完成后再执行后续操作时,可以使用
dispatch_group
。例如,同时发起多个网络请求,待所有请求完成后进行数据合并处理:
dispatch_group_t group = dispatch_group_create();
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_group_async(group, queue, ^{
// 第一个网络请求
NSURL *url1 = [NSURL URLWithString:@"https://example.com/api1"];
NSData *data1 = [NSData dataWithContentsOfURL:url1];
});
dispatch_group_async(group, queue, ^{
// 第二个网络请求
NSURL *url2 = [NSURL URLWithString:@"https://example.com/api2"];
NSData *data2 = [NSURL URLWithString:url2];
});
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
// 合并数据并更新 UI
});
2. 避免多线程环境下块使用时的资源竞争问题
- 使用串行队列: 如果多个任务访问共享资源,将这些任务提交到同一个串行队列中,这样可以保证任务顺序执行,避免资源竞争。例如,有一个共享的计数器:
dispatch_queue_t serialQueue = dispatch_queue_create("com.example.serialQueue", DISPATCH_QUEUE_SERIAL);
__block int counter = 0;
dispatch_async(serialQueue, ^{
counter++;
});
dispatch_async(serialQueue, ^{
counter--;
});
- 锁机制:
在多线程环境下,使用锁来保护共享资源。以
NSLock
为例:
NSLock *lock = [[NSLock alloc] init];
__block int sharedValue = 0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
sharedValue++;
[lock unlock];
});
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[lock lock];
sharedValue--;
[lock unlock];
});
- 原子属性:
如果共享资源是对象的属性,可以将属性声明为原子(
atomic
)的。例如:
@interface MyClass : NSObject
@property (nonatomic, atomic, strong) NSString *sharedString;
@end
@implementation MyClass
@end
这样在多线程环境下访问 sharedString
时,会自动进行线程安全的操作,但原子属性会带来一定的性能开销。
3. 性能瓶颈及优化策略
- 性能瓶颈:
- 频繁创建和销毁队列:频繁创建和销毁队列会带来额外的开销。
- 锁竞争:如果锁的粒度太大或持有锁的时间过长,会导致线程等待,降低性能。
- 过度的异步任务:过多的异步任务可能导致系统资源耗尽,尤其是在内存和 CPU 方面。
- 优化策略:
- 复用队列:尽量复用系统提供的全局队列或创建少量的自定义队列,而不是频繁创建和销毁。
- 减小锁粒度:只在访问共享资源的关键代码段加锁,并且尽量缩短持有锁的时间。例如:
NSLock *lock = [[NSLock alloc] init];
__block int sharedValue = 0;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
int temp = 0;
// 先在无锁状态下进行部分计算
temp = sharedValue + 1;
[lock lock];
sharedValue = temp;
[lock unlock];
});
- **控制异步任务数量**:可以使用信号量(`dispatch_semaphore`)来控制同时执行的异步任务数量。例如,限制同时最多有 3 个网络请求在执行:
dispatch_semaphore_t semaphore = dispatch_semaphore_create(3);
NSArray *urls = @[@"https://example.com/api1", @"https://example.com/api2", @"https://example.com/api3", @"https://example.com/api4"];
for (NSString *urlString in urls) {
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSURL *url = [NSURL URLWithString:urlString];
NSData *data = [NSData dataWithContentsOfURL:url];
dispatch_semaphore_signal(semaphore);
});
}