MST

星途 面试题库

面试题:Objective-C在复杂iOS视图控制器管理场景下的架构设计与优化

设想一个具有多层级视图控制器嵌套,并且存在频繁视图切换、数据交互的复杂iOS应用场景。请基于Objective - C语言,设计一套合理的视图控制器管理架构,并阐述如何优化以提高应用的性能和可维护性。同时,请说明在该架构下如何处理跨模块视图控制器间的通信以及应对视图控制器的动态创建与销毁。
27.8万 热度难度
编程语言Objective-C

知识考点

AI 面试

面试题答案

一键面试

一、视图控制器管理架构设计

  1. 基于导航控制器(UINavigationController)和标签栏控制器(UITabBarController)的基础架构

    • 导航控制器:用于管理具有层级关系的视图控制器,实现页面的前进和后退。例如,在一个新闻应用中,从新闻列表页(一个视图控制器)点击进入新闻详情页(另一个视图控制器),就可以通过导航控制器来管理这种层级关系。
    • 标签栏控制器:用于在不同功能模块间进行切换,比如社交应用中,通过标签栏切换“消息”“联系人”“动态”等不同模块。
  2. 自定义容器视图控制器(Container View Controller)

    • 对于复杂的多层级嵌套场景,可以自定义容器视图控制器。在自定义容器视图控制器中,可以通过addChildViewController:方法添加子视图控制器,并通过transitionFromViewController:toViewController:duration:options:animations:completion:方法来管理子视图控制器之间的切换动画。
    • 例如,在一个电商应用中,商品详情页可能有多个子模块,如商品介绍、评论、规格选择等,这些子模块可以由不同的视图控制器管理,通过自定义容器视图控制器来管理它们的显示和切换。
  3. 视图控制器工厂模式

    • 创建一个视图控制器工厂类(如ViewControllerFactory),负责创建视图控制器实例。这样可以将视图控制器的创建逻辑集中管理,方便代码维护和复用。
    • 例如:
@interface ViewControllerFactory : NSObject
+ (instancetype)sharedFactory;
- (UIViewController *)createViewControllerWithIdentifier:(NSString *)identifier;
@end

@implementation ViewControllerFactory
+ (instancetype)sharedFactory {
    static ViewControllerFactory *factory = nil;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        factory = [[ViewControllerFactory alloc] init];
    });
    return factory;
}

- (UIViewController *)createViewControllerWithIdentifier:(NSString *)identifier {
    if ([identifier isEqualToString:@"HomeViewController"]) {
        return [[HomeViewController alloc] initWithNibName:@"HomeViewController" bundle:nil];
    } else if ([identifier isEqualToString:@"DetailViewController"]) {
        return [[DetailViewController alloc] initWithNibName:@"DetailViewController" bundle:nil];
    }
    return nil;
}
@end

二、性能和可维护性优化

  1. 性能优化

    • 延迟加载:对于不常用或加载耗时的视图控制器,采用延迟加载的方式。在需要显示该视图控制器时才进行创建和加载,避免在应用启动时就创建大量不必要的视图控制器,减少内存占用和启动时间。
    • 视图重用:在自定义容器视图控制器或导航控制器管理的视图控制器栈中,对于已经创建过且符合复用条件的视图控制器,进行复用。例如,在一个聊天列表和聊天详情页的场景中,聊天详情页视图控制器在返回列表页后,可以将其保留在内存中,当下次再进入聊天详情页时,如果满足一定条件(如聊天对象相同等),可以复用之前的视图控制器实例。
    • 优化视图切换动画:避免使用过于复杂或耗时的动画效果。可以使用系统提供的简单且高效的动画选项,如UIViewAnimationOptionCurveEaseInOut等,来实现流畅的视图切换动画。同时,控制动画的持续时间,不宜过长,以减少用户等待时间。
  2. 可维护性优化

    • 模块化设计:将不同功能模块的视图控制器分开管理,每个模块有自己独立的视图控制器类和相关的业务逻辑。例如,将电商应用中的商品模块、订单模块、用户模块等的视图控制器分别放在不同的文件夹或命名空间中,便于代码的查找和修改。
    • 清晰的代码结构:在每个视图控制器类中,按照功能将代码进行分组,如视图加载、数据请求、事件处理等。使用注释清晰地标明每个代码块的作用,提高代码的可读性。
    • 依赖注入:通过依赖注入的方式,将视图控制器所依赖的对象(如数据模型、网络请求对象等)传递给视图控制器,而不是在视图控制器内部直接创建。这样可以提高代码的可测试性和可维护性,例如:
@interface DetailViewController : UIViewController
@property (nonatomic, strong) id<DataModelProtocol> dataModel;
- (instancetype)initWithDataModel:(id<DataModelProtocol>)dataModel;
@end

@implementation DetailViewController
- (instancetype)initWithDataModel:(id<DataModelProtocol>)dataModel {
    self = [super initWithNibName:nil bundle:nil];
    if (self) {
        _dataModel = dataModel;
    }
    return self;
}
@end

三、跨模块视图控制器间的通信

  1. 通知中心(NSNotificationCenter)
    • 当一个视图控制器需要向其他多个视图控制器发送消息时,可以使用通知中心。例如,在一个多模块的应用中,用户在设置模块修改了主题颜色,需要通知所有涉及界面显示的模块更新颜色。
    • 发送通知:
[[NSNotificationCenter defaultCenter] postNotificationName:@"ThemeColorChangedNotification" object:nil userInfo:@{@"themeColor": newThemeColor}];
- 接收通知:
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateThemeColor:) name:@"ThemeColorChangedNotification" object:nil];
- (void)updateThemeColor:(NSNotification *)notification {
    UIColor *themeColor = notification.userInfo[@"themeColor"];
    // 更新界面颜色的逻辑
}
  1. 代理模式(Delegate Pattern)
    • 当两个视图控制器之间存在明确的一对一通信关系时,适合使用代理模式。例如,在一个登录模块和主界面模块之间,登录成功后需要通知主界面模块进行用户信息的更新。
    • 定义代理协议:
@protocol LoginViewControllerDelegate <NSObject>
- (void)loginViewControllerDidLoginSuccessfully:(LoginViewController *)loginViewController;
@end
- 在登录视图控制器中设置代理并在登录成功时调用代理方法:
@interface LoginViewController : UIViewController
@property (nonatomic, weak) id<LoginViewControllerDelegate> delegate;
@end

@implementation LoginViewController
- (void)loginButtonTapped {
    // 登录逻辑
    if (loginSuccess) {
        [self.delegate loginViewControllerDidLoginSuccessfully:self];
    }
}
@end
- 在主界面视图控制器中遵循代理协议并实现代理方法:
@interface MainViewController : UIViewController <LoginViewControllerDelegate>
@end

@implementation MainViewController
- (void)loginViewControllerDidLoginSuccessfully:(LoginViewController *)loginViewController {
    // 更新用户信息的逻辑
}
@end
  1. 回调闭包(Block)
    • 对于简单的跨模块视图控制器间的通信,特别是在有返回值的情况下,可以使用回调闭包。例如,在一个选择图片的视图控制器中,选择图片后返回图片数据给调用者视图控制器。
    • 在选择图片视图控制器中定义闭包属性:
@interface ImagePickerViewController : UIViewController
@property (nonatomic, copy) void(^imagePickedBlock)(UIImage *pickedImage);
@end

@implementation ImagePickerViewController
- (void)imagePickerController:(UIImagePickerController *)picker didFinishPickingMediaWithInfo:(NSDictionary<NSString *,id> *)info {
    UIImage *pickedImage = info[UIImagePickerControllerOriginalImage];
    if (self.imagePickedBlock) {
        self.imagePickedBlock(pickedImage);
    }
    [self dismissViewControllerAnimated:YES completion:nil];
}
@end
- 在调用者视图控制器中设置闭包并处理返回数据:
ImagePickerViewController *imagePickerVC = [[ImagePickerViewController alloc] init];
imagePickerVC.imagePickedBlock = ^(UIImage *pickedImage) {
    // 处理选择的图片的逻辑
};
[self presentViewController:imagePickerVC animated:YES completion:nil];

四、视图控制器的动态创建与销毁

  1. 动态创建

    • 使用前面提到的视图控制器工厂模式来动态创建视图控制器。根据不同的业务需求,通过调用工厂类的方法来创建相应的视图控制器实例。例如,在一个根据用户权限显示不同功能页面的应用中,根据用户权限的判断结果,调用ViewControllerFactorycreateViewControllerWithIdentifier:方法来创建对应的视图控制器。
    • 在创建视图控制器时,同时设置好其所需的属性和依赖关系。如在创建商品详情视图控制器时,将商品的唯一标识或数据模型传递给该视图控制器,以便其正确显示商品详情信息。
  2. 动态销毁

    • 在视图控制器不再需要时,需要进行适当的销毁操作以释放内存。对于通过导航控制器管理的视图控制器,当调用popViewControllerAnimated:方法时,视图控制器会被自动从导航栈中移除并释放内存(前提是没有其他强引用指向该视图控制器)。
    • 对于自定义容器视图控制器管理的子视图控制器,在移除子视图控制器时,需要调用removeFromParentViewController方法,并在子视图控制器的dealloc方法中进行必要的资源释放操作,如取消网络请求、移除通知中心的观察者等。
    • 例如:
- (void)removeChildViewController:(UIViewController *)childVC {
    [childVC willMoveToParentViewController:nil];
    [childVC.view removeFromSuperview];
    [childVC removeFromParentViewController];
}

在子视图控制器的dealloc方法中:

- (void)dealloc {
    [[NSNotificationCenter defaultCenter] removeObserver:self];
    // 取消网络请求等资源释放操作
}