美文网首页
OC中基类强制要求子类实现相应方法

OC中基类强制要求子类实现相应方法

作者: 肠粉白粥_Hoben | 来源:发表于2020-06-08 14:49 被阅读0次

在最近的需求中,为了方便后续拓展,将相应的View和Model都抽离成了比较通用的基类,并在collectionView写好了通用的读取和加载方法,后续如果有扩展业务只需继承相应的基类,并且实现预设好的方法即可。

for (HobenBaseModel *model in modelArray) {
    BOOL hasInitCurrentView = YES;
    if (!self.viewDict) {
        self.viewDict = [NSMutableDictionary dictionary];
    }
    id view = [self.viewDict safeObjectForKey:model.type];
    if (!view) {
        view = [activityModel createView];
        hasInitCurrentView = NO;
    }
    if ([view respondsToSelector:@selector(setModel:)]) {
        [view performSelector:@selector(setModel:) withObject:model];
    }
    [self.viewDict safeSetObject:view forKey:model.type];
}

但是作为基类,如何引导后续开发者实现必须实现的方法呢?写注释可以,但是如果漏看注释就会导致一些问题,因此需要基类强制要求子类实现对应的方法。

比如,现在BaseModel有一个强制要求子Model实现的createView方法,用于对应View的初始化,后续开发只需在子Model实现该方法即可。那么我们可以这样做:

@interface HobenBaseModel : NSObject

@property (nonatomic, strong) NSString *type;

- (__kindof HobenBaseView *)createView;

@end

@implementation HobenBaseModel

- (__kindof HobenBaseView *)createView {
    NSAssert(false, @"继承该类需要声明createView方法");
    return [[HobenBaseView alloc] init];
}

@end

这样来做,如果子Model在implementation没有实现createView方法的话,就会进入断言:Thread 1: Exception: "继承该类需要声明createView方法",提醒开发者实现该方法。

子类Model必须实现对应的createView方法:

@implementation HobenSonModel

- (HobenSonView *)createView {
    HobenSonView *view = [[HobenSonView alloc] init];
    return view;
}

@end

同样地,BaseView也需要提醒子类,如果需要设置数据源的话,就得命名为model:

@interface HobenBaseView : UIView <NSCopying>

// 需要命名为model且定义@synthesize model = _model;
@property (strong, nonatomic) __kindof HobenBaseModel *model;

@end

@implementation HobenBaseView

@dynamic model;

- (id)copyWithZone:(NSZone *)zone {
    id view = [[[self class] alloc] init];
    if ([view respondsToSelector:@selector(setModel:)]) {
        if ([self respondsToSelector:@selector(model)]) {
            id model = [self performSelector:@selector(model)];
            [view performSelector:@selector(setModel:) withObject:model];
        }
    }
    return view;
}

@end

这样一来,子View实现起来就得这样写:

@interface HobenSonView ()

@property (nonatomic, strong) HobenSonModel *model;

@end

@implementation HobenSonView

@synthesize model = _model;

- (void)setModel:(HobenSonModel *)model {
    _model = model;
    // ...
}

@end

如果子View不实现@synthesize model = _model;setter的话跑起来就会报错:Thread 1: Exception: "-[HobenSonView model]: unrecognized selector sent to instance 0x7fa6911c1750"

这是因为我在父类里面声明了这个model是@dynamic的,即不会自动生成getter和setter,需要自行实现,而子类实现的@synthesize的语义是如果你没有手动实现setter方法和getter方法,那么编译器会自动为你加上这两个方法。

这样一来,不规范的写法就会被编译器报错了,后续加的子类即插即用,不需要修改父类的内容,而是需要遵循父类引导的准则来写,应该是比较符合设计原则的。


更新于8.19

后续有大佬同事跟进了这个需求,发现写在collectionView里面的代码还是太过于复杂,而且子view和model的命名、相应方法和继承均受到限制,对于后续拓展不太好,故需要整理一下代码的放置位置。

他的做法如下:

setModel:方法放在Model内,即

@implementation HobenSonModel

- (HobenSonView *)createView {
    HobenSonView *view = [[HobenSonView alloc] init];
    [view setModel:self];
    return view;
}

@end

collectionView获取View的方法如下:

for (HobenBaseModel *model in modelArray) {
    BOOL hasInitCurrentView = YES;
    if (!self.viewDict) {
        self.viewDict = [NSMutableDictionary dictionary];
    }
    id view = [self.viewDict safeObjectForKey:model.type];
    if (!view) {
        if ([model isKindOfClass:[HobenSonModel class]]) {
            view = [model createView];
        }
        hasInitCurrentView = NO;
    }
    [self.viewDict safeSetObject:view forKey:model.type];
}

另外,由于旧版本不能显示新版本的视图,所以需要加个标识符,代表是否需要处理后端返回的字段,这个字段最好也是放在Model里面,用Protocol处理:

@protocol HobenSubViewProtocol <NSObject>

// 数据 model 对应的 activity_type 字段(banner 分类)取值
+ (NSString *)subView_activityType;

@end

@interface HobenSonModel : NSObject <HobenSubViewProtocol>

- (HobenSonModel *)createView;

@end

@implementation HobenSonModel

- (HobenSonView *)createView {
    HobenSonView *view = [[HobenSonView alloc] init];
    [view setModel:self];
    return view;
}

#pragma mark - HobenSubViewProtocol

+ (NSString *)subView_activityType {
    return @"sub_view1";
}

@end

解析数据也只解析符合的

NSMutableArray<id<HobenSubViewProtocol>> *tmpArr = [NSMutableArray array];
[data enumerateObjectsUsingBlock:^(id  _Nonnull obj, NSUInteger idx, BOOL * _Nonnull stop) {
    NSString *activity_type = [obj objectForKey:@"activity_type"];
    // 便于后续扩展,只处理 activity_type 成功匹配的数据
    if ([activity_type isEqualToString:[HobenSonModel subView_activityType]]) {
        id model = [HobenSonModel fromJSONDictionary:obj];
        [tmpArr addObject:model];
    }
}];

如果后续要加新业务,则可以自定义Model、View,实现Protocol和对应方法,并在解析数据、collectionView上面加相关判断就行了,其实工作量也不是很大,感觉也还行,最主要还是对应业务放在对应Model、View里面实现较好。

相关文章

  • OC中基类强制要求子类实现相应方法

    在最近的需求中,为了方便后续拓展,将相应的View和Model都抽离成了比较通用的基类,并在collectionV...

  • Objective-c 子类继承父类私有方法

    笔记: 在OC中 如果子类重写了父类的私有方法,父类不会再调用本类的实现,而是直接调用子类的实现。切记,切记。

  • iOS 小知识点总结

    子类实现父类方法时,监测子类是否调用super方法。 在父类中声明方法时: 子类中实现该父类方法: 图片压缩

  • 设计模式-行为模式-模板方法模式

    模板方法模式是在基类中定义操作的步骤分解(算法骨架),在子类中实现具体的单个步骤的模式。 以下代码定义了基类,基类...

  • OC 中子类如何调用父类的私有方法

    OC中能实现子类调用父类的私有方法吗? 调用父类的私有方法无非是想做两种操作:1.父类的实现完全不适用于子类(需完...

  • PHP设计模式之工厂模式

    工厂模式的实现 简单的工厂模式由三部分组成: 抽象基类:类中定义抽象一些方法,用以在子类中实现; 继承自抽象基类的...

  • iOS-面向协议编程

    用工厂方法时,先定义了一个基类,在基类的声明中定义了一系列的方法,类实现里面并不需要实现,而是在子类里面实现的,需...

  • <<设计模式之禅(第二版)>>——第二章

    定义: 所有引用基类的地方必须能透明地使用其子类的对象。 基本使用原则: 子类必须完全实现父类的方法(ps:在类中...

  • C++ 虚函数、纯虚函数、虚基类、抽象类

    纯虚函数:纯虚函数是在基类中声明的虚函数,它在基类中没有定义,但要求任何派生类都要定义自己的实现方法。在基类中实现...

  • 重写类方法

    有时候开发总会遇见要重写类方法的时候,那么今天我们就来谈谈如何实现类方法吧,原理如下: 1、在子类中实现一个同基类...

网友评论

      本文标题:OC中基类强制要求子类实现相应方法

      本文链接:https://www.haomeiwen.com/subject/idinohtx.html