面试题答案
一键面试1. 模板方法模式设计
1.1 定义抽象基类
定义一个抽象类,包含模板方法以及抽象的具体步骤方法。
#import <Foundation/Foundation.h>
@interface NetworkTask : NSObject
// 模板方法,定义任务执行流程
- (void)executeTask;
// 抽象方法,具体任务需实现
- (void)sendRequest;
- (void)parseData;
- (void)storeResult;
@end
@implementation NetworkTask
- (void)executeTask {
[self sendRequest];
[self parseData];
[self storeResult];
}
// 抽象方法,留给子类实现
- (void)sendRequest {
@throw [NSException exceptionWithName:@"UnimplementedMethod" reason:@"sendRequest must be implemented by subclasses" userInfo:nil];
}
- (void)parseData {
@throw [NSException exceptionWithName:@"UnimplementedMethod" reason:@"parseData must be implemented by subclasses" userInfo:nil];
}
- (void)storeResult {
@throw [NSException exceptionWithName:@"UnimplementedMethod" reason:@"storeResult must be implemented by subclasses" userInfo:nil];
}
@end
1.2 定义具体子类
具体子类继承自抽象基类,并实现抽象方法。
@interface SpecificNetworkTask : NetworkTask
@property (nonatomic, strong) NSURLSessionDataTask *dataTask;
@end
@implementation SpecificNetworkTask
- (void)sendRequest {
NSURL *url = [NSURL URLWithString:@"https://example.com/api"];
NSURLRequest *request = [NSURLRequest requestWithURL:url];
self.dataTask = [[NSURLSession sharedSession] dataTaskWithRequest:request completionHandler:^(NSData * _Nullable data, NSURLResponse * _Nullable response, NSError * _Nullable error) {
if (!error && data) {
// 这里假设主线程处理解析和存储,需要dispatch到主线程
dispatch_async(dispatch_get_main_queue(), ^{
[self parseDataWithData:data];
[self storeResult];
});
}
}];
[self.dataTask resume];
}
- (void)parseDataWithData:(NSData *)data {
// 具体的数据解析逻辑
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:data options:0 error:nil];
NSLog(@"Parsed data: %@", json);
}
- (void)storeResult {
// 具体的结果存储逻辑
NSLog(@"Result stored.");
}
@end
2. 多线程同步处理
2.1 使用队列和锁
在多线程环境下,对于共享资源的访问需要同步。例如,如果多个网络请求任务可能会访问同一个数据库进行结果存储,可以使用NSLock
或dispatch_semaphore
。
// 使用NSLock示例
NSLock *databaseLock = [[NSLock alloc] init];
- (void)storeResult {
[databaseLock lock];
// 数据库存储操作
[databaseLock unlock];
}
2.2 使用GCD队列
使用串行队列来保证任务顺序执行,避免竞争条件。例如,在NetworkTask
类中,可以定义一个串行队列来处理任务。
@interface NetworkTask ()
@property (nonatomic, strong) dispatch_queue_t taskQueue;
@end
@implementation NetworkTask
- (instancetype)init {
self = [super init];
if (self) {
_taskQueue = dispatch_queue_create("com.example.networkTaskQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
- (void)executeTask {
dispatch_async(self.taskQueue, ^{
[self sendRequest];
[self parseData];
[self storeResult];
});
}
@end
3. 内存管理
3.1 ARC(自动引用计数)
在现代Objective - C开发中,ARC已经大大简化了内存管理。确保对象的生命周期被正确管理,例如在网络请求完成后,及时释放不再需要的对象。对于NSURLSessionDataTask
,当任务完成后,dataTask
属性可以被释放(如果没有其他强引用)。
3.2 手动管理
在某些情况下,如使用Core Foundation框架,可能需要手动管理内存。使用CFRelease
等函数来释放Core Foundation对象。
4. 潜在问题及解决方案
4.1 死锁
- 问题描述:如果多个线程相互等待对方释放锁,就会发生死锁。例如,线程A持有锁1并等待锁2,而线程B持有锁2并等待锁1。
- 解决方案:避免嵌套锁,按照固定顺序获取锁,或者使用超时机制,在一定时间内获取不到锁就放弃。
4.2 数据竞争
- 问题描述:多个线程同时访问和修改共享资源,导致数据不一致。
- 解决方案:使用同步机制,如上述提到的锁和队列,确保同一时间只有一个线程访问共享资源。
4.3 内存泄漏
- 问题描述:对象不再被使用,但没有被释放,导致内存占用不断增加。
- 解决方案:遵循ARC规则,确保对象在不再需要时被正确释放。对于手动管理的对象,及时调用释放函数。同时,可以使用 Instruments中的Leaks工具来检测内存泄漏。