面试题答案
一键面试1. 代码架构
在iOS应用开发中,通常采用分层架构,将网络请求、数据处理和UI更新分开处理。例如,使用MVC(Model - View - Controller)或者MVVM(Model - View - ViewModel)架构。
- 网络请求层:负责与服务器进行数据交互,通常使用
NSURLSession
,它本身已经是基于多线程的。可以使用队列来管理请求,例如NSOperationQueue
。 - 数据处理层:对从网络获取的数据进行处理,可能涉及到复杂的计算和数据转换。这一层可以在后台线程执行,避免阻塞主线程。
- UI更新层:所有的UI更新操作必须在主线程执行,以保证界面的流畅性和一致性。
2. 资源管理
- 共享资源:在多线程环境下,共享资源(如数据库、缓存)需要特别注意同步访问。例如,若多个线程同时读写数据库,可能导致数据不一致。可以使用锁(如互斥锁
pthread_mutex
)或者信号量来保护共享资源。 - 内存管理:多线程操作可能会导致内存管理问题,如内存泄漏或过度释放。在ARC(自动引用计数)环境下,虽然编译器会自动处理大部分内存管理,但仍需注意对象在不同线程的生命周期。例如,确保在对象被释放前,所有对它的操作都已完成。
3. 性能调优
- 减少锁的竞争:锁的使用会降低性能,因为同一时间只有一个线程可以获取锁。尽量缩短锁的持有时间,将非关键操作放在锁外执行。
- 使用合适的线程模型:根据具体场景选择合适的线程模型,如GCD(Grand Central Dispatch)、NSOperationQueue或pthread。GCD简单易用,适合简单的异步任务;NSOperationQueue更灵活,可以设置依赖关系;pthread则更底层,适合对线程控制要求较高的场景。
4. 不同场景下的同步策略及代码示例
场景一:网络请求与数据处理
假设我们有一个网络请求,获取到数据后需要进行处理,然后更新UI。
#import <UIKit/UIKit.h>
#import <Foundation/Foundation.h>
@interface ViewController : UIViewController
@end
@implementation ViewController
- (void)viewDidLoad {
[super viewDidLoad];
// 创建一个信号量
dispatch_semaphore_t semaphore = dispatch_semaphore_create(0);
// 网络请求在后台线程执行
dispatch_queue_t networkQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(networkQueue, ^{
// 模拟网络请求
NSURLSessionDataTask *task = [[NSURLSession sharedSession] dataTaskWithURL:[NSURL URLWithString:@"https://example.com/api/data"] completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (data &&!error) {
// 数据处理
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
// 数据处理完成,发送信号量
dispatch_semaphore_signal(semaphore);
}
}];
[task resume];
});
// 等待网络请求和数据处理完成
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER);
// 在主线程更新UI
dispatch_async(dispatch_get_main_queue(), ^{
// 这里进行UI更新操作,例如更新一个UILabel的文本
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(100, 100, 200, 50)];
label.text = @"数据已更新";
[self.view addSubview:label];
});
}
@end
在这个场景中,使用信号量来确保网络请求和数据处理完成后再进行UI更新。
场景二:多线程访问共享资源(如数据库)
假设我们有一个简单的数据库操作类,多个线程可能同时访问。
#import <Foundation/Foundation.h>
@interface DatabaseManager : NSObject
@property (nonatomic, strong) NSMutableArray *dataArray;
@property (nonatomic, assign) pthread_mutex_t mutex;
- (instancetype)init;
- (void)addData:(id)data;
- (id)getDataAtIndex:(NSUInteger)index;
@end
@implementation DatabaseManager
- (instancetype)init {
self = [super init];
if (self) {
_dataArray = [NSMutableArray array];
pthread_mutex_init(&_mutex, NULL);
}
return self;
}
- (void)addData:(id)data {
pthread_mutex_lock(&_mutex);
[self.dataArray addObject:data];
pthread_mutex_unlock(&_mutex);
}
- (id)getDataAtIndex:(NSUInteger)index {
id result = nil;
pthread_mutex_lock(&_mutex);
if (index < self.dataArray.count) {
result = self.dataArray[index];
}
pthread_mutex_unlock(&_mutex);
return result;
}
- (void)dealloc {
pthread_mutex_destroy(&_mutex);
}
@end
在这个场景中,使用互斥锁pthread_mutex
来保护共享的NSMutableArray
,确保多线程访问时的数据一致性。
场景三:基于条件变量的线程同步
假设我们有一个任务队列,一个线程负责生成任务,另一个线程负责处理任务。
#import <pthread.h>
#import <stdio.h>
#import <stdlib.h>
#define MAX_QUEUE_SIZE 5
typedef struct {
int data[MAX_QUEUE_SIZE];
int head;
int tail;
int count;
pthread_mutex_t mutex;
pthread_cond_t notFull;
pthread_cond_t notEmpty;
} TaskQueue;
void initQueue(TaskQueue *queue) {
queue->head = 0;
queue->tail = 0;
queue->count = 0;
pthread_mutex_init(&queue->mutex, NULL);
pthread_cond_init(&queue->notFull, NULL);
pthread_cond_init(&queue->notEmpty, NULL);
}
void enqueue(TaskQueue *queue, int value) {
pthread_mutex_lock(&queue->mutex);
while (queue->count == MAX_QUEUE_SIZE) {
pthread_cond_wait(&queue->notFull, &queue->mutex);
}
queue->data[queue->tail] = value;
queue->tail = (queue->tail + 1) % MAX_QUEUE_SIZE;
queue->count++;
pthread_cond_signal(&queue->notEmpty);
pthread_mutex_unlock(&queue->mutex);
}
int dequeue(TaskQueue *queue) {
pthread_mutex_lock(&queue->mutex);
while (queue->count == 0) {
pthread_cond_wait(&queue->notEmpty, &queue->mutex);
}
int value = queue->data[queue->head];
queue->head = (queue->head + 1) % MAX_QUEUE_SIZE;
queue->count--;
pthread_cond_signal(&queue->notFull);
pthread_mutex_unlock(&queue->mutex);
return value;
}
void* producer(void* arg) {
TaskQueue *queue = (TaskQueue*)arg;
for (int i = 0; i < 10; i++) {
enqueue(queue, i);
printf("Produced: %d\n", i);
}
return NULL;
}
void* consumer(void* arg) {
TaskQueue *queue = (TaskQueue*)arg;
for (int i = 0; i < 10; i++) {
int value = dequeue(queue);
printf("Consumed: %d\n", value);
}
return NULL;
}
int main() {
TaskQueue queue;
initQueue(&queue);
pthread_t producerThread, consumerThread;
pthread_create(&producerThread, NULL, producer, &queue);
pthread_create(&consumerThread, NULL, consumer, &queue);
pthread_join(producerThread, NULL);
pthread_join(consumerThread, NULL);
pthread_mutex_destroy(&queue.mutex);
pthread_cond_destroy(&queue.notFull);
pthread_cond_destroy(&queue.notEmpty);
return 0;
}
在这个场景中,使用条件变量pthread_cond
来实现生产者 - 消费者模型,确保任务队列在满或空时线程的正确等待和唤醒。
通过以上方式,在不同场景下选择合适的同步策略,可以在保证线程安全的同时最大程度提升性能。