基于MVVM+RAC架构实践

阅读原文

一、基类构建

Protocols模块

  • Protocols.h
#import "CellProtocol.h"
#import "ViewProtocol.h"
#import "ViewModelProtocol.h"
#import "ControllerProtocol.h"
  • CellProtocol.h
@protocol CellProtocol 

@optional

/*
 * 初始化UI控件
 */
- (void)setupViews;

@end
  • ViewProtocol.h
@protocol ViewModelProtocol;

@protocol ViewProtocol 

@optional

- (instancetype)initWithViewModel:(id)viewModel;
/**
 * 初始化UI控件
 */
- (void)setupViews;
/**
 * 初始化RAC
 */
- (void)racViewModel;

@end
  • ControllerProtocol.h
@protocol ViewModelProtocol;

@protocol ControllerProtocol 

@optional

- (instancetype)initWithViewModel:(id )viewModel;
/*
 * 初始化UI控件
 */
- (void)addSubviews;
/*
 * 初始化RAC
 */
- (void)racViewModel;
/*
 * 初始化导航栏
 */
- (void)layoutNavigation;

@end
  • ViewModelProtocol.h
@protocol ViewModelProtocol 

@optional

- (instancetype)initWithModel:(id)model;

/**
 *  初始化
 */
- (void)initialize;

@end

Controller模块

  • YSBaseViewController.h
#import 
#import "ControllerProtocol.h"

NS_ASSUME_NONNULL_BEGIN

@interface YSBaseViewController : UIViewController

@end

NS_ASSUME_NONNULL_END
  • YSBaseViewController.m
#import "YSBaseViewController.h"

@interface YSBaseViewController ()

@end

@implementation YSBaseViewController

+ (instancetype)allocWithZone:(struct _NSZone *)zone{
    YSBaseViewController *viewController = [super allocWithZone:zone];
    @weakify(viewController)
    [[viewController rac_signalForSelector:@selector(viewDidLoad)] subscribeNext:^(id x) {
        @strongify(viewController)
        [viewController layoutNavigation];
        [viewController addSubviews];
        [viewController racViewModel];
    }];
    return viewController;
}

- (instancetype)initWithViewModel:(id )viewModel{
    if (self = [super init]) {
    }
    return self;
}

- (void)addSubviews{
    self.view.backgroundColor = UIColorViewBG;
}

- (void)racViewModel{}

- (void)layoutNavigation{
    [self setLeftBarButtonWithTitle:nil];
}

- (void)viewDidLoad {
    [super viewDidLoad];
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(1 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
        NSLog(@"当前控制器 ---   %@  --- ",NSStringFromClass([self class]));
    });
}

- (void)dealloc{
    NSLog(@"VC -- %@ -- 正常销毁",NSStringFromClass([self class]));
}

@end

View模块

  • YSBaseView.h
#import 
#import "ViewProtocol.h"

NS_ASSUME_NONNULL_BEGIN

@interface YSBaseView : UIView

@end

NS_ASSUME_NONNULL_END
  • YSBaseView.m
#import "YSBaseView.h"

@implementation YSBaseView

- (instancetype)init{
    if (self = [super init]) {
        [self racViewModel];
        [self setupViews];
    }
    return self;
}

- (instancetype)initWithViewModel:(id)viewModel{
    if (self = [super init]) {
        [self racViewModel];
        [self setupViews];
    }
    return self;
}

- (void)bindViewModel{}

- (void)setupViews{}

- (void)dealloc{
    NSLog(@"YSBaseView -- %@ -- 正常销毁",NSStringFromClass([self class]));
}

@end
  • YSBaseTableViewCell.h
#import 
#import "CellProtocol.h"

NS_ASSUME_NONNULL_BEGIN

@interface YSBaseTableViewCell : UITableViewCell

@end

NS_ASSUME_NONNULL_END
  • YSBaseTableViewCell.m
#import "YSBaseTableViewCell.h"

@implementation YSBaseTableViewCell

- (void)awakeFromNib {
    [super awakeFromNib];
}

- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
    [super setSelected:selected animated:animated];
}

- (instancetype)initWithStyle:(UITableViewCellStyle)style reuseIdentifier:(NSString *)reuseIdentifier {
    if (self = [super initWithStyle:style reuseIdentifier:reuseIdentifier]) {
        [self setupViews];
    }
    return self;
}

- (void)setupViews{}

- (void)dealloc{
    NSLog(@"YSBaseTableViewCell -- %@ -- 正常销毁",NSStringFromClass([self class]));
}

@end
  • YSBaseCollectionViewCell.h
#import 
#import "CellProtocol.h"

NS_ASSUME_NONNULL_BEGIN

@interface YSBaseCollectionViewCell : UICollectionViewCell

@end

NS_ASSUME_NONNULL_END
  • YSBaseCollectionViewCell.m
#import "YSBaseCollectionViewCell.h"

@implementation YSBaseCollectionViewCell

- (instancetype)initWithFrame:(CGRect)frame{
    if (self = [super initWithFrame:frame]){
        [self setupViews];
    }
    return self;
}

- (void)setupViews{}

- (void)dealloc{
    NSLog(@"YSBaseCollectionViewCell -- %@ -- 正常销毁",NSStringFromClass([self class]));
}

@end

ViewModel模块

  • YSBaseViewModel.h
#import 
#import "ViewModelProtocol.h"

NS_ASSUME_NONNULL_BEGIN

@interface YSBaseViewModel : NSObject

@end

NS_ASSUME_NONNULL_END
  • YSBaseViewModel.m

#import "YSBaseViewModel.h"

@implementation YSBaseViewModel

+ (instancetype)allocWithZone:(struct _NSZone *)zone {
    YSBaseViewModel *viewModel = [super allocWithZone:zone];
    if (viewModel) {
        [viewModel initialize];
    }
    return viewModel;
}

- (instancetype)initWithModel:(id)model {
    if (self = [super init]) {
    }
    return self;
}

- (void)initialize{
    
}

@end

Model模块

  • YSBaseModel.h
#import 

NS_ASSUME_NONNULL_BEGIN

@interface YSBaseModel : NSObject

@end

NS_ASSUME_NONNULL_END
  • YSBaseModel.m
#import "YSBaseModel.h"
#include 

@implementation YSBaseModel

- (NSString *)debugDescription{
    return [self description];
}

- (NSString *)description{
    if ([self isKindOfClass:[NSArray class]] || [self isKindOfClass:[NSDictionary class]] || [self isKindOfClass:[NSNumber class]] || [self isKindOfClass:[NSString class]])
    {
        return self.debugDescription;
    }
    NSMutableDictionary *dictionary = [NSMutableDictionary dictionary];
    uint count;
    objc_property_t *properties = class_copyPropertyList([self class], &count);
    for (int i = 0; i < count; I++)
    {
        objc_property_t property = properties[I];
        NSString *name = @(property_getName(property));
        id value = [self valueForKey:name]?:@"nil";
        [dictionary setObject:value forKey:name];
    }
    free(properties);
    return [NSString stringWithFormat:@"<%@: %p> -- %@", [self class], self, dictionary];
}

- (void)dealloc{
    NSLog(@"YSBaseModel -- %@ -- 正常销毁",NSStringFromClass([self class]));
}

@end

二、实践示例

模块结构

  • HomeModel.h
#import "YSBaseModel.h"

NS_ASSUME_NONNULL_BEGIN

@interface HomeModel : YSBaseModel

@property (nonatomic,copy) NSString *title;

@end

NS_ASSUME_NONNULL_END
  • HomeModel.m
#import "HomeModel.h"

@implementation HomeModel

@end
  • HomeView.h
#import "YSBaseView.h"

NS_ASSUME_NONNULL_BEGIN

@interface HomeView : YSBaseView

@end

NS_ASSUME_NONNULL_END
  • HomeView.m
#import "HomeView.h"
#import "HomeViewModel.h"

@interface HomeView()

@property (nonatomic,strong) UITableView *homeTableView;
@property (nonatomic,strong) HomeViewModel *homeViewModel;

@end

@implementation HomeView

- (instancetype)initWithViewModel:(id)viewModel{
    self.homeViewModel = (HomeViewModel *)viewModel;
    return [super initWithViewModel:viewModel];
}

#pragma mark - *********** initViews ***********

- (void)setupViews{
    [self addSubview:self.homeTableView];
    self.homeTableView
    .sd_layout
    .spaceToSuperView(UIEdgeInsetsMake(0, 0, 0, 0));
}

#pragma mark - *********** RAC ***********

- (void)racViewModel{
    @weakify(self)
    [[self.homeViewModel.loadDataSubject takeUntil:self.rac_willDeallocSignal] subscribeNext:^(id  _Nullable x) {
        @strongify(self)
        [self.homeTableView reloadData];
    }];
}
    
#pragma mark - *********** UITabelViewDelegate ***********

- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section{
    return self.homeViewModel.dataArray.count;
}

- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath{
    static NSString *reuseIdetify = @"TableViewCell";
    UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:reuseIdetify];
    if (!cell) {
        cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:reuseIdetify];
        cell.selectionStyle = UITableViewCellSelectionStyleNone;
    }
    
    HomeModel *model = self.homeViewModel.dataArray[indexPath.row];
    
    cell.textLabel.text = model.title;
    
    return cell;
}

#pragma mark - *********** lazy ***********

- (UITableView *)homeTableView{
    if (!_homeTableView) {
        _homeTableView = [[UITableView alloc] init];
        _homeTableView.backgroundColor = UIColorViewBG;
        _homeTableView.tableFooterView = [[UIView alloc] init];
        _homeTableView.dataSource = self;
        _homeTableView.delegate = self;
        
        if (@available(iOS 11.0, *)) {
            _homeTableView.contentInsetAdjustmentBehavior = UIScrollViewContentInsetAdjustmentNever;
        }
    }
    return _homeTableView;
}

- (HomeViewModel *)homeViewModel{
    if (!_homeViewModel) {
        _homeViewModel = [[HomeViewModel alloc] init];
    }
    return _homeViewModel;
}

@end
  • HomeViewModel.h
#import "YSBaseViewModel.h"
#import "HomeModel.h"

NS_ASSUME_NONNULL_BEGIN

@interface HomeViewModel : YSBaseViewModel

@property (nonatomic,strong) RACSubject *loadDataSubject;
@property (nonatomic,strong) RACCommand *loadDataCommand;

@property (nonatomic,strong) NSMutableArray *dataArray;

@end

NS_ASSUME_NONNULL_END
  • HomeViewModel.m
#import "HomeViewModel.h"

@implementation HomeViewModel

- (void)initialize{
    @weakify(self)
    [self.loadDataCommand.executionSignals.switchToLatest subscribeNext:^(id  _Nullable x) {
        @strongify(self)
        if (x == nil) {
            [self.loadDataSubject sendNext:nil];
            return;
        }
        if ([x[@"success"] boolValue]) {
            NSArray *arr = x[@"body"];
            if ([arr isKindOfClass:[NSArray class]]) {
                if (self.dataArray) {
                    [self.dataArray removeAllObjects];
                }
                [self.dataArray addObjectsFromArray:arr];
            }
            [self.loadDataSubject sendNext:@"请求成功"];
        }else{
            [self.loadDataSubject sendNext:@"请求失败"];
        }
    }];
}

- (RACCommand *)loadDataCommand{
    if (!_loadDataCommand) {
        _loadDataCommand = [[RACCommand alloc] initWithSignalBlock:^RACSignal * _Nonnull(id  _Nullable input) {
            return [RACSignal createSignal:^RACDisposable * _Nullable(id  _Nonnull subscriber) {
                
                //mock网络请求获取数据
                NSMutableArray *arr = @[].mutableCopy;
                for (int i = 0 ; i < 10 ; i++){
                    HomeModel *home = HomeModel.new;
                    home.title = [NSString stringWithFormat:@"home : %d",I];
                    [arr addObject:home];
                }
                
                [subscriber sendNext:@{@"success":@"1",@
                                       "body":arr.copy
                                       }];
                [subscriber sendCompleted];
                
                return nil;
            }];
        }];
    }
    return _loadDataCommand;
}

- (RACSubject *)loadDataSubject{
    if (!_loadDataSubject) {
        _loadDataSubject = [RACSubject subject];
    }
    return _loadDataSubject;
}

- (NSMutableArray *)dataArray{
    if (!_dataArray) {
        _dataArray = [[NSMutableArray alloc] init];
    }
    return _dataArray;
}

@end
  • HomeViewController.h
#import "YSBaseViewController.h"

NS_ASSUME_NONNULL_BEGIN

@interface HomeViewController : YSBaseViewController

@end

NS_ASSUME_NONNULL_END
  • HomeViewController.m
#import "HomeViewController.h"
#import "HomeView.h"
#import "HomeViewModel.h"

@interface HomeViewController ()

@property (nonatomic,strong) HomeView *homeView;
@property (nonatomic,strong) HomeViewModel *homeViewModel;

@end

@implementation HomeViewController

#pragma mark - *********** initViews ***********

- (void)addSubviews{
    self.title = @"MVVM示例";
    [self.view addSubview:self.homeView];
    self.homeView.sd_layout.spaceToSuperView(UIEdgeInsetsMake(0, 0, 0, 0));
}

#pragma mark - *********** lazy ***********

- (HomeView *)homeView{
    if (!_homeView) {
        _homeView = [[HomeView alloc] initWithViewModel:self.homeViewModel];
    }
    return _homeView;
}

- (HomeViewModel *)homeViewModel{
    if (!_homeViewModel) {
        _homeViewModel = [[HomeViewModel alloc] init];
        [_homeViewModel.loadDataCommand execute:nil];
    }
    return _homeViewModel;
}

@end

效果

总结

此示例结合了MVVM+RAC做了模型绑定,数据传递的演示,需要了解RAC的几个概念

  • RACCommand 可理解为行为执行,执行什么事情
  • RACSubject 可理解为得到结果之后干什么
  • RAC功能很强大,很多功能值得大家探索

以上均是自学过程的积累,学到哪记到哪

原创文章,转载请注明出处,谢谢!

你可能感兴趣的:(基于MVVM+RAC架构实践)