美文网首页
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中基类强制要求子类实现相应方法

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