重用我为非常相似但不相同的情况制作的 UIViewController 子类:推荐吗?

Reusing a UIViewController subclass I've made for very similar but not identical situations: recommended?

我对 iOS 开发还比较陌生,想知道这样做是否是一个好习惯。

例如,假设我有两个不同的上下文,我想在其中以 identical/similar 方式呈现 identical/similar 数据,但存在一些差异,可能是从一个上下文到另一个上下文的一些不同按钮或者我需要做的不同的事情来准备数据。我应该只在同一个视图控制器中创建多个配置方法并根据需要调用其中一个,还是有两个单独的视图控制器?

我非常倾向于前者是正确的,因为它看起来更高效并且节省了大量相同的代码,但我希望更有经验的程序员提供一些意见以防万一。

您应该创建三个视图控制器:SuperViewControllerFirstViewControllerSecondViewControllerFirstViewControllerSecondViewController 应该是 SuperViewController 的子类。那么你应该考虑以下几点:

变量和属性

SuperViewController 中声明视图控制器之间的所有公共变量。

在适当的视图控制器中声明任何视图控制器特定的变量和属性。

方法

与属性一样,在 SuperViewController 中定义任何共享方法及其实现。

如果您的方法在您的视图控制器之间共享一些但不是全部代码,请执行以下操作:

1) 在您的 SuperViewController 中实施此方法:

-(void)someSemiSharedMethod {
    // put the common code here
}

2) 在您的视图控制器中:

-(void)someSemiSharedMethod {
    // call the superclass' implementation of this method
    // to ensure that common code is executed
    [super someSemiSharedMethod];

    // now add any child controller specific code to this method below
}

这似乎是实现您想要实现的目标的最佳方法。如果您有其他问题或者此方法不能完全达到您的要求,请告诉我。

您可以执行 Andriy 描述的父超类方法,但还有另一种选择。

子类化非常强大,但有时很难推理。如果其中一个子视图控制器中存在错误,则很难找出错误的真正所在。

如果对视图控制器的自定义很小,您可以使用构造函数的参数来表达,例如:

- (instancetype)initWithSaveButton(BOOL)includeSaveButton;

或者,如果您需要自定义某些行为,请传递一个块:

- (instancetype)initWithSaveAction:(void (^)(Person *))saveAction;

如果自定义很少,我发现这是比子类化更简洁的解决方案。


编辑:旁注,但@leiyun 问了一个关于 BOOL 标志的好问题——如果我们想在包括按钮 A 和按钮 B 之间做出选择怎么办?我们应该设计一个这样的初始化方法吗?

- (instancetype)initWithButtonA:(BOOL)includeA buttonB:(BOOL)includeB;

嗯,这是一个很好的例子,说明为什么应该避免使用 BOOL 标志。如果我们像 initWithButtonA:YES buttonB:YES 这样调用此方法,那么我们将进入未定义行为,因为我们想在 这些按钮之间选择

为此,定义一个选项类型。

typedef enum {
    MyViewControllerModeNoButtons,
    MyViewControllerModeButtonA,
    MyViewControllerModeButtonB
} MyViewControllerMode;

- (instancetype)initWithMode:(MyViewControllerMode)mode;

像这样的接口的指导原则是调用它的方式不应该是错误的。此外,当您需要第三个按钮或图像视图或其他东西时,这将更好地容纳更多选项!

为了完整起见,我还想提出在 Andriy 和 joerick 的回答之后我认为的第三种选择,特别是对于“我需要做的不同的事情来准备数据"-场景:实际上对两个屏幕使用相同的视图控制器class,但为保存、准备和提供适当数据的每个上下文创建单独的数据源对象。

这些数据源对象可以是简单的 NSObject 子classes,符合协议。对于显示数据的常用方式,已经为数据源预定义了协议,例如 UITableViewDataSourceUICollectionViewDataSource。如果这些不适合您的需要,您当然可以定义自己的协议。数据源可以由视图控制器实例保存为 属性,如下所示:

@property (strong, nonatomic) id<UITableViewDataSource> myDataSource;

您可以 "give" 在初始化时为您的视图控制器提供适当的数据源,例如

-(instancetype)initWithDataSource: (id<UITableViewDataSource>) dataSource