MVP+Context模式

2023-04-06  本文已影响0人  人生若只如初见丶_a4e8

记录下自己项目中使用的MVP+Context模式。

1、传统的 MVP 和MVP+Context的区别

MVP.png

2、Context的实现

@interface XJContext : NSObject

///p层 逻辑业务
@property (nonatomic, strong) XJPresenter *presenter;
///view层UI展示
@property (nonatomic, strong) XJBaseView *view;
///UI响应
@property (nonatomic, strong) XJInteractor *interactor;

@end
@interface XWeakObjectContainer : NSObject

@property (nonatomic, readonly, weak) id weakObject;

- (instancetype)initWithWeakObject:(id)object;
@end

@interface NSObject (Context)

@property (nonatomic, strong, nullable) XJContext *context;
@end
- (instancetype)initWithWeakObject:(id)object {
    self = [super init];
    if (self) {
        _weakObject = object;
    }
    
    return self;
}

@end

@interface NSObject ()
/// 弱网时presenter的context被controller释放,造成内测泄漏,访问已释放对象。
/// 或者控制器销毁的时候将presenter的context置nil。
//@property (nonatomic ,strong) NSHashTable *family;

@end

@implementation NSObject (Context)

- (void)setContext:(XJContext *)objc {
    // 通过NSHashTable判断
//    if (!self.family) {
//        self.family = [NSHashTable hashTableWithOptions:NSHashTableWeakMemory];
//        [self.family addObject:objc];
//    }
    XWeakObjectContainer *container = [[XWeakObjectContainer alloc] initWithWeakObject:objc];
      objc_setAssociatedObject(self, @selector(context), container, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}

- (XJContext *)context {
    // 通过NSHashTable判断
//    if (self.family.allObjects.count == 0){
//        return nil;
//    }

    XWeakObjectContainer *container = objc_getAssociatedObject(self, @selector(context));
    id curContext = container.weakObject;
    
    if (curContext == nil && [self isKindOfClass:[UIView class]]) {
        UIView *view = (UIView *)self;
        UIView *supView = view.superview;
        while (supView != nil) {
            if (supView.context != nil) {
                curContext = supView.context;
                break;
            }
            supView = supView.superview;
        }
        
        if (curContext != nil) {
            [self setContext:curContext];
        }
    }
    return curContext;
}
@interface UIViewController (MVPConfig)

@property (nonatomic, strong) XJContext *rootContext;
@property (nonatomic, assign) BOOL mvpEnable;

/// 注册MVP
- (void)configMVP:(NSString *)name;
- (void)configMVP;

@end
    self.rootContext = [[XJContext alloc] init]; //strong
    self.context = self.rootContext;//weak
 //presenter
    Class presenterClass = NSClassFromString(xj_StringFormat(@"%@Presenter", name));
    if (presenterClass != NULL) {
        self.context.presenter = [presenterClass new];
        self.context.presenter.context = self.context;
    }
    
    //interactor
    Class interactorClass = NSClassFromString(xj_StringFormat(@"%@Interactor", name));
    if (interactorClass != NULL) {
        self.context.interactor = [interactorClass new];
        self.context.interactor.context = self.context;
    }
    
    //view
    Class viewClass = NSClassFromString(xj_StringFormat(@"%@View", name));
    if (viewClass != NULL) {
//        NSString *str = [[NSBundle mainBundle] pathForResource:StringFormat(@"%@View", name) ofType:@"nib"];
//        if (!str) {
            self.context.view = [viewClass new];
//        } else {
//            self.context.view = (XJBaseView *)NSBundleloadNibNamed(NSStringFormat(@"%@View", name));
//        }
        self.context.view.context = self.context;
    } else {
        XJBaseView *view = XJBaseView.new;
        self.context.view = view;
        self.context.view.context = self.context;
    }
    self.context.presenter.view = self.context.view;
    self.context.presenter.baseController = self;

//    self.context.interactor.view = self.context.view;
//    self.context.interactor.baseController = self;

    self.context.view.presenter = self.context.presenter;
   if (curContext == nil && [self isKindOfClass:[UIView class]]) {
        UIView *view = (UIView *)self;
        UIView *supView = view.superview;
        while (supView != nil) {
            if (supView.context != nil) {
                curContext = supView.context;
                break;
            }
            supView = supView.superview;
        }
        
        if (curContext != nil) {
            [self setContext:curContext];
        }
    }

3、使用方法

- (void)viewDidLoad {
    [self configMVP:@"YCYHHomeGas"];
    [super viewDidLoad];

}
@protocol XJPresenterDelegate <NSObject>

@required

@optional

/// 创建视图
- (void)buildView;
/// 创建视图
- (void)buildView:(nullable XJBaseAdapter *)adapter;
/// 加载数据
- (void)loadData:(nullable XJBaseAdapter *)adapter;
/// 加载更多数据
- (void)loadMoreData;
/// 加载更多数据
- (void)loadMoreData:(nullable XJBaseAdapter *)adapter;
/// 刷新UI
- (void)reloadUI;
/// 刷新UI
- (void)reloadUIWithData:(nullable id)data;
/// 返回
- (void)gotoBack;
/// 跳转
- (void)gotoDetail:(nullable id)data;

@end


@interface XJPresenter : NSObject <XJPresenterDelegate>

@property (nonatomic, weak) XJBaseView *view;
@property (nonatomic, weak) UIViewController *baseController;

@end

继承的Presenter通过添加更多的协议来满足项目需求,协议过多时还可通过Interactor,来进行更进一步的职责划分

按照:VC需要界面展示-> XJ(self.context.view, XJPresenterDelegate, buildView);->界面需要数据内容-> XJ(self.context.presenter, XJPresenterDelegate, loadData:self.adapter);-> 最后刷新界面赋值-> XJ(self.context.view, XJPresenterDelegate, reloadUI); 这样的方式展开交互。

其中adapter作为通信的数据模型类,保存着所有的模型数据

参考文章和代码:
ios架构--MVP

上一篇 下一篇

猜你喜欢

热点阅读