面试题答案
一键面试启动优化
- 减少启动时的计算任务:
- 在
AppDelegate
的application:didFinishLaunchingWithOptions:
方法中,避免执行复杂的计算任务,如大量的初始化逻辑、数据预处理等。将非必要的任务延迟到用户操作或者后台线程执行。 - 例如,如果有一些数据需要从本地文件读取并进行复杂解析,可以使用
dispatch_async
将其放到后台队列中执行。
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 复杂的数据读取和解析逻辑 NSString *filePath = [[NSBundle mainBundle] pathForResource:@"data" ofType:@"txt"]; NSString *dataString = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]; // 解析数据 NSArray *parsedData = [dataString componentsSeparatedByString:@","]; // 处理完后回到主线程更新UI(如果需要) dispatch_async(dispatch_get_main_queue(), ^{ // 更新UI }); });
- 在
- 优化动态库加载:
- 尽量减少动态库的依赖,动态库加载会增加启动时间。如果必须使用动态库,确保其初始化过程简单高效。
- 对于Objective - C语言,可以使用
@lazy_import
来延迟加载动态库。例如,对于一个非关键的第三方库:
@lazy_import(<OptionalLibrary/OptionalLibrary.h>)
- 利用runtime机制延迟方法实现:
- 在类的实现中,可以使用runtime的关联对象(Associated Objects)来延迟一些方法的具体实现。例如,有一个复杂的初始化方法
initComplex
,可以先不实现,通过runtime在需要时动态添加实现。
@interface MyClass : NSObject - (void)initComplex; @end @implementation MyClass + (void)load { Method originalMethod = class_getInstanceMethod(self, @selector(initComplex)); IMP originalIMP = method_getImplementation(originalMethod); const char *originalType = method_getTypeEncoding(originalMethod); // 定义一个简单的占位实现 IMP placeholderIMP = imp_implementationWithBlock(^(id selfObject) { // 这里可以记录日志或者直接返回 NSLog(@"initComplex is called, but will be implemented later."); return nil; }); class_replaceMethod(self, @selector(initComplex), placeholderIMP, originalType); } - (void)realInitComplex { // 真正的复杂初始化逻辑 } - (void)whenNeeded { // 当需要时,动态替换为真正的实现 Method realMethod = class_getInstanceMethod(self.class, @selector(realInitComplex)); IMP realIMP = method_getImplementation(realMethod); class_replaceMethod(self.class, @selector(initComplex), realIMP, method_getTypeEncoding(realMethod)); [self initComplex]; } @end
- 在类的实现中,可以使用runtime的关联对象(Associated Objects)来延迟一些方法的具体实现。例如,有一个复杂的初始化方法
滚动列表优化
- 复用单元格:
- 在
UITableView
或UICollectionView
中,使用单元格复用机制。在UITableViewDataSource
的tableView:cellForRowAtIndexPath:
方法中,通过dequeueReusableCellWithIdentifier:forIndexPath:
获取复用单元格。
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellIdentifier" forIndexPath:indexPath]; // 配置单元格数据 cell.textLabel.text = [NSString stringWithFormat:@"Row %ld", (long)indexPath.row]; return cell; }
- 在
- 异步加载单元格数据:
- 当单元格的数据需要从网络或者数据库加载时,使用GCD或者
NSOperationQueue
进行异步加载。 - 使用GCD示例:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellIdentifier" forIndexPath:indexPath]; dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ // 异步加载数据 NSString *data = [self loadDataForIndexPath:indexPath]; dispatch_async(dispatch_get_main_queue(), ^{ // 回到主线程更新单元格 cell.textLabel.text = data; }); }); return cell; }
- 使用
NSOperationQueue
示例:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"CellIdentifier" forIndexPath:indexPath]; NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ // 异步加载数据 NSString *data = [self loadDataForIndexPath:indexPath]; dispatch_async(dispatch_get_main_queue(), ^{ // 回到主线程更新单元格 cell.textLabel.text = data; }); }]; [[NSOperationQueue mainQueue] addOperation:operation]; return cell; }
- 当单元格的数据需要从网络或者数据库加载时,使用GCD或者
- 优化单元格绘制:
- 避免在单元格的
drawRect:
方法中执行复杂的绘制操作。如果需要绘制复杂图形,可以使用Core Animation
提前渲染到图片,然后在单元格中显示图片。 - 例如,使用
UIBezierPath
和CAShapeLayer
提前绘制一个复杂图形到图片:
UIBezierPath *path = [UIBezierPath bezierPathWithOvalInRect:CGRectMake(0, 0, 50, 50)]; CAShapeLayer *layer = [CAShapeLayer layer]; layer.path = path.CGPath; layer.fillColor = [UIColor redColor].CGColor; UIGraphicsBeginImageContext(CGSizeMake(50, 50)); [layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *image = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); cell.imageView.image = image;
- 避免在单元格的
加载大量数据优化
- 分批加载:
- 从数据库或者网络加载大量数据时,采用分批加载的方式。例如,在从数据库查询数据时,使用
LIMIT
和OFFSET
关键字(对于SQLite数据库)。 - 在Objective - C中,结合
FMDB
库进行分批查询:
FMDatabase *db = [FMDatabase databaseWithPath:databasePath]; if ([db open]) { int pageSize = 100; int offset = 0; while (true) { FMResultSet *rs = [db executeQuery:@"SELECT * FROM YourTable LIMIT ? OFFSET ?", @(pageSize), @(offset)]; while ([rs next]) { // 处理数据 NSString *data = [rs stringForColumn:@"ColumnName"]; } [rs close]; if (rs.columnCount == 0) { break; } offset += pageSize; } [db close]; }
- 从数据库或者网络加载大量数据时,采用分批加载的方式。例如,在从数据库查询数据时,使用
- 使用缓存:
- 对于频繁访问的数据,使用缓存机制。可以使用
NSCache
(类似于NSDictionary
,但具有自动释放内存的特性)或者SDWebImage
等第三方缓存库(用于图片缓存)。 - 使用
NSCache
示例:
static NSCache *dataCache; + (NSCache *)dataCache { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ dataCache = [[NSCache alloc] init]; }); return dataCache; } - (NSString *)loadDataForIndex:(NSUInteger)index { NSString *cachedData = [[self class] dataCache][@(index)]; if (cachedData) { return cachedData; } // 从其他地方加载数据 NSString *newData = [self loadDataFromSource:index]; [[self class] dataCache][@(index)] = newData; return newData; }
- 对于频繁访问的数据,使用缓存机制。可以使用
- 多线程加载与处理:
- 使用GCD或
NSOperationQueue
进行多线程加载和处理数据。 - 使用GCD进行并行加载多个数据块:
dispatch_queue_t concurrentQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); NSArray *dataIndices = @[@0, @1, @2, @3]; dispatch_group_t group = dispatch_group_create(); for (NSNumber *index in dataIndices) { dispatch_group_async(group, concurrentQueue, ^{ // 加载和处理数据 NSString *data = [self loadDataForIndex:index.unsignedIntegerValue]; // 处理数据 NSString *processedData = [self processData:data]; // 回到主线程更新UI(如果需要) dispatch_async(dispatch_get_main_queue(), ^{ // 更新UI }); }); } dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 所有任务完成后的操作 });
- 使用
NSOperationQueue
实现类似功能:
NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; operationQueue.maxConcurrentOperationCount = 4; NSArray *dataIndices = @[@0, @1, @2, @3]; NSMutableArray<NSBlockOperation *> *operations = [NSMutableArray array]; for (NSNumber *index in dataIndices) { NSBlockOperation *operation = [NSBlockOperation blockOperationWithBlock:^{ // 加载和处理数据 NSString *data = [self loadDataForIndex:index.unsignedIntegerValue]; NSString *processedData = [self processData:data]; // 回到主线程更新UI(如果需要) dispatch_async(dispatch_get_main_queue(), ^{ // 更新UI }); }]; [operations addObject:operation]; } for (NSBlockOperation *operation in operations) { [operationQueue addOperation:operation]; } [operationQueue addOperationWithBlock:^{ // 所有任务完成后的操作 }];
- 使用GCD或
利用runtime机制进行动态优化
- 方法交换:
- 可以使用runtime的
method_exchangeImplementations
函数进行方法交换,实现对系统方法或者自定义方法的动态替换。例如,要在所有UIViewController
的viewDidLoad
方法中添加日志记录:
@interface UIViewController (Logging) @end @implementation UIViewController (Logging) + (void)load { Method originalMethod = class_getInstanceMethod(self, @selector(viewDidLoad)); Method swizzledMethod = class_getInstanceMethod(self, @selector(swizzled_viewDidLoad)); method_exchangeImplementations(originalMethod, swizzledMethod); } - (void)swizzled_viewDidLoad { NSLog(@"%@'s viewDidLoad is called.", self.class); [self swizzled_viewDidLoad]; } @end
- 可以使用runtime的
- 动态添加属性:
- 通过runtime的关联对象可以在运行时为类动态添加属性。例如,为
UIButton
添加一个自定义的identifier
属性:
@interface UIButton (CustomIdentifier) @property (nonatomic, copy) NSString *customIdentifier; @end @implementation UIButton (CustomIdentifier) static const char *customIdentifierKey = "customIdentifierKey"; - (NSString *)customIdentifier { return objc_getAssociatedObject(self, customIdentifierKey); } - (void)setCustomIdentifier:(NSString *)customIdentifier { objc_setAssociatedObject(self, customIdentifierKey, customIdentifier, OBJC_ASSOCIATION_COPY_NONATOMIC); } @end
- 通过runtime的关联对象可以在运行时为类动态添加属性。例如,为
多线程环境下利用GCD与NSOperationQueue优化性能
- GCD:
- 并行执行任务:使用
dispatch_apply
可以方便地并行执行一系列任务。例如,计算数组中每个元素的平方:
NSArray *numbers = @[@1, @2, @3, @4]; NSMutableArray *squaredNumbers = [NSMutableArray arrayWithCapacity:numbers.count]; dispatch_apply(numbers.count, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(size_t index) { NSNumber *number = numbers[index]; NSNumber *squared = @(number.integerValue * number.integerValue); dispatch_sync(dispatch_get_main_queue(), ^{ [squaredNumbers addObject:squared]; }); });
- 控制任务执行顺序:可以使用
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, ^{ // 任务1 NSLog(@"Task 1 started."); sleep(2); NSLog(@"Task 1 finished."); }); dispatch_group_async(group, queue, ^{ // 任务2 NSLog(@"Task 2 started."); sleep(1); NSLog(@"Task 2 finished."); }); dispatch_group_notify(group, dispatch_get_main_queue(), ^{ // 汇总任务 NSLog(@"All tasks finished, starting summary task."); });
- 并行执行任务:使用
- NSOperationQueue:
- 创建和添加操作:可以创建
NSOperation
的子类或者使用NSBlockOperation
来定义任务,然后添加到NSOperationQueue
中。例如:
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"Operation 1 started."); sleep(2); NSLog(@"Operation 1 finished."); }]; NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"Operation 2 started."); sleep(1); NSLog(@"Operation 2 finished."); }]; NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; [operationQueue addOperation:operation1]; [operationQueue addOperation:operation2];
- 设置操作依赖:可以通过
addDependency:
方法设置操作之间的依赖关系。例如,让operation2
依赖于operation1
:
NSBlockOperation *operation1 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"Operation 1 started."); sleep(2); NSLog(@"Operation 1 finished."); }]; NSBlockOperation *operation2 = [NSBlockOperation blockOperationWithBlock:^{ NSLog(@"Operation 2 started."); sleep(1); NSLog(@"Operation 2 finished."); }]; [operation2 addDependency:operation1]; NSOperationQueue *operationQueue = [[NSOperationQueue alloc] init]; [operationQueue addOperation:operation1]; [operationQueue addOperation:operation2];
- 创建和添加操作:可以创建