面试题答案
一键面试可能出现的问题
- 性能问题
- 视图加载缓慢:多层嵌套导航意味着可能有大量视图需要加载,若视图加载逻辑复杂,如包含大量自定义绘制、网络请求获取数据用于填充视图等操作,会导致主线程阻塞,用户体验卡顿。
- 滚动性能差:当多层视图嵌套且包含可滚动视图(如
UITableView
、UIScrollView
等)时,如果布局不合理或者视图重用机制不完善,会导致滚动时帧率下降。
- 内存管理问题
- 内存泄漏:在多层嵌套导航中,视图控制器之间可能存在强引用循环。例如,父视图控制器持有子视图控制器的强引用,同时子视图控制器又通过代理或其他方式持有父视图控制器的强引用,这会导致两个视图控制器都无法被释放,造成内存泄漏。
- 内存峰值过高:大量视图控制器及其相关视图的创建和加载,如果没有及时释放不再使用的视图控制器和视图,会导致应用程序内存占用持续上升,达到过高的峰值,甚至可能引发系统内存警告,导致应用被强制关闭。
优化策略
- 视图加载和卸载优化
- 延迟加载:对于非当前可见的视图,采用延迟加载的方式。比如,在
UITableView
的cellForRowAtIndexPath:
方法中,只有当单元格即将显示时才加载其复杂视图。可以使用NSObject
的关联对象(Associated Objects)来实现懒加载视图。
- (UIView *)lazyLoadedView { UIView *view = objc_getAssociatedObject(self, @selector(lazyLoadedView)); if (!view) { view = [[UIView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)]; // 复杂的视图初始化逻辑 objc_setAssociatedObject(self, @selector(lazyLoadedView), view, OBJC_ASSOCIATION_RETAIN_NONATOMIC); } return view; }
- 视图重用:在可滚动视图(如
UITableView
、UICollectionView
)中,正确使用重用机制。注册可重用的单元格类,并在需要时从队列中取出重用,避免重复创建。
static NSString *CellIdentifier = @"MyCell"; UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier]; if (!cell) { cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier]; }
- 卸载视图:在视图控制器即将消失时,及时卸载不需要的视图。例如,在
viewWillDisappear:
方法中,将一些临时创建且不再需要的视图从父视图中移除,并释放相关资源。
- (void)viewWillDisappear:(BOOL)animated { [super viewWillDisappear:animated]; [self.temporaryView removeFromSuperview]; self.temporaryView = nil; }
- 延迟加载:对于非当前可见的视图,采用延迟加载的方式。比如,在
- 处理不同层级视图控制器交互对生命周期的影响
- 合理设置引用关系:避免强引用循环。在视图控制器之间传递数据或设置代理时,确保引用关系正确。例如,如果子视图控制器需要回调父视图控制器的方法,将父视图控制器设置为子视图控制器的代理时,子视图控制器对代理的引用应该是弱引用(
weak
)。
@protocol ChildViewControllerDelegate <NSObject> - (void)childViewControllerDidFinish:(ChildViewController *)childVC; @end @interface ChildViewController : UIViewController @property (nonatomic, weak) id<ChildViewControllerDelegate> delegate; @end
- 生命周期方法调用链:在不同层级视图控制器中,确保生命周期方法的正确调用。例如,当父视图控制器添加或移除子视图控制器时,要在合适的生命周期方法中进行操作,并且通知子视图控制器相应的生命周期变化。在父视图控制器的
addChildViewController:
方法调用后,要手动调用子视图控制器的didMoveToParentViewController:
方法。
- (void)addChildViewController:(UIViewController *)childVC { [super addChildViewController:childVC]; [self.view addSubview:childVC.view]; [childVC didMoveToParentViewController:self]; }
- 数据传递优化:在不同层级视图控制器之间传递数据时,尽量避免频繁的大规模数据传递。可以使用单例模式(
Singleton
)或者通知中心(NSNotificationCenter
)来实现数据共享和通信,但要注意在不需要时及时移除通知观察者,防止内存泄漏。
// 使用单例共享数据 MyDataSingleton *singleton = [MyDataSingleton sharedInstance]; // 使用通知中心 [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(receiveNotification:) name:@"MyNotification" object:nil]; // 在不需要时移除观察者 [[NSNotificationCenter defaultCenter] removeObserver:self name:@"MyNotification" object:nil];
- 合理设置引用关系:避免强引用循环。在视图控制器之间传递数据或设置代理时,确保引用关系正确。例如,如果子视图控制器需要回调父视图控制器的方法,将父视图控制器设置为子视图控制器的代理时,子视图控制器对代理的引用应该是弱引用(