iOS组件化方案
一、蘑菇街url-block
方案
这是蘑菇街中应用的一种页面间调用的方式,通过在启动时注册组件提供的服务,把调用组件使用的url
和组件提供的服务block
对应起来,保存到内存中。在使用组件的服务时,通过url
找到对应的block
,然后获取服务。
具体实现代码如下:
//Mediator.m 中间件
@implementation Mediator
typedef void (^componentBlock) (id param);
@property (nonatomic, storng) NSMutableDictionary *cache
- (void)registerURLPattern:(NSString *)urlPattern toHandler:(componentBlock)blk {
[cache setObject:blk forKey:urlPattern];
}
- (void)openURL:(NSString *)url withParam:(id)param {
componentBlock blk = [cache objectForKey:url];
if (blk) blk(param);
}
@end
1.先在Mediator中注册url-block
#import "Mediator.h"
#import "WRBookDetailViewController.h"
+ (void)initComponent {
[[Mediator sharedInstance] registerURLPattern:@"weread://bookDetail" toHandler:^(NSDictionary *param) {
WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:param[@"bookId"]];
[[UIApplication sharedApplication].keyWindow.rootViewController.navigationController pushViewController:detailVC animated:YES];
}];
}
2.通过url在中间件中就能找到对应的block,并执行跳转
#import "Mediator.h"
+ (void)gotoDetail:(NSString *)bookId {
[[Mediator sharedInstance] openURL:@"weread://bookDetail" withParam:@{@"bookId": bookId}];
}
二、 蘑菇街protocol-class方案
针对url-block
方案无法传递非正规参数,新增一种protocol-class方案
//ProtocolMediator.m 中间件
@implementation ProtocolMediator
@property (nonatomic, storng) NSMutableDictionary *protocolCache
- (void)registerProtocol:(Protocol *)proto forClass:(Class)cls {
NSMutableDictionary *protocolCache;
[protocolCache setObject:cls forKey:NSStringFromProtocol(proto)];
} - (Class)classForProtocol:(Protocol *)proto {
return protocolCache[NSStringFromProtocol(proto)];
}
@end
每一个组件都有一个公共Protocol文件,定义了每一个组件对外提供的接口
//ComponentProtocol.h
@protocol BookDetailComponentProtocol <NSObject>
- (UIViewController *)bookDetailController:(NSString *)bookId;
- (UIImage *)coverImageWithBookId:(NSString *)bookId;
@end @protocol ReviewComponentProtocol <NSObject>
- (UIViewController *)ReviewController:(NSString *)bookId;
@end //BookDetailComponent 组件
#import "ProtocolMediator.h"
#import "ComponentProtocol.h"
#import "WRBookDetailViewController.h"
+ (void)initComponent{
[[ProtocolMediator sharedInstance] registerProtocol:@protocol(BookDetailComponentProtocol) forClass:[self class];
} - (UIViewController *)bookDetailController:(NSString *)bookId {
WRBookDetailViewController *detailVC = [[WRBookDetailViewController alloc] initWithBookId:param[@"bookId"]];
return detailVC;
} - (UIImage *)coverImageWithBookId:(NSString *)bookId {
….
}
最后调用者通过 protocol 从 ProtocolMediator 拿到提供这些方法的 Class,再进行调用:
//WRReadingViewController.m 调用者
//ReadingViewController.m
#import "ProtocolMediator.h"
#import "ComponentProtocol.h"
+ (void)gotoDetail:(NSString *)bookId {
Class cls = [[ProtocolMediator sharedInstance] classForProtocol:BookDetailComponentProtocol];
id bookDetailComponent = [[cls alloc] init];
UIViewController *vc = [bookDetailComponent bookDetailController:bookId];
[self.navigationController pushViewController:vc animated:YES];
}
三、target-action方案
1.先设置一个中间件公共类
@interface CTMediator : NSObject + (instancetype)sharedInstance; // 远程App调用入口
- (id)performActionWithUrl:(NSURL *)url completion:(void(^)(NSDictionary *info))completion;
// 本地组件调用入口
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget;
- (void)releaseCachedTargetWithTargetName:(NSString *)targetName; @end //
// CTMediator.m
// CTMediator
//
// Created by casa on 16/3/13.
// Copyright © 2016年 casa. All rights reserved.
// #import "CTMediator.h" @interface CTMediator () @property (nonatomic, strong) NSMutableDictionary *cachedTarget; @end @implementation CTMediator #pragma mark - public methods
+ (instancetype)sharedInstance
{
static CTMediator *mediator;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
mediator = [[CTMediator alloc] init];
});
return mediator;
} /*
scheme://[target]/[action]?[params] url sample:
aaa://targetA/actionB?id=1234
*/ - (id)performActionWithUrl:(NSURL *)url completion:(void (^)(NSDictionary *))completion
{
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
NSString *urlString = [url query];
for (NSString *param in [urlString componentsSeparatedByString:@"&"]) {
NSArray *elts = [param componentsSeparatedByString:@"="];
if([elts count] < 2) continue;
[params setObject:[elts lastObject] forKey:[elts firstObject]];
} // 这里这么写主要是出于安全考虑,防止黑客通过远程方式调用本地模块。这里的做法足以应对绝大多数场景,如果要求更加严苛,也可以做更加复杂的安全逻辑。
NSString *actionName = [url.path stringByReplacingOccurrencesOfString:@"/" withString:@""];
if ([actionName hasPrefix:@"native"]) {
return @(NO);
} // 这个demo针对URL的路由处理非常简单,就只是取对应的target名字和method名字,但这已经足以应对绝大部份需求。如果需要拓展,可以在这个方法调用之前加入完整的路由逻辑
id result = [self performTarget:url.host action:actionName params:params shouldCacheTarget:NO];
if (completion) {
if (result) {
completion(@{@"result":result});
} else {
completion(nil);
}
}
return result;
} - (id)performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget
{ NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName]; id target = self.cachedTarget[targetClassString];
if (target == nil) {
Class targetClass = NSClassFromString(targetClassString);
target = [[targetClass alloc] init];
} SEL action = NSSelectorFromString(actionString); if (target == nil) {
// 这里是处理无响应请求的地方之一,这个demo做得比较简单,如果没有可以响应的target,就直接return了。实际开发过程中是可以事先给一个固定的target专门用于在这个时候顶上,然后处理这种请求的
return nil;
} if (shouldCacheTarget) {
self.cachedTarget[targetClassString] = target;
} if ([target respondsToSelector:action]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
} else {
// 这里是处理无响应请求的地方,如果无响应,则尝试调用对应target的notFound方法统一处理
SEL action = NSSelectorFromString(@"notFound:");
if ([target respondsToSelector:action]) {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
} else {
// 这里也是处理无响应请求的地方,在notFound都没有的时候,这个demo是直接return了。实际开发过程中,可以用前面提到的固定的target顶上的。
[self.cachedTarget removeObjectForKey:targetClassString];
return nil;
}
}
} - (void)releaseCachedTargetWithTargetName:(NSString *)targetName
{
NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
[self.cachedTarget removeObjectForKey:targetClassString];
} #pragma mark - getters and setters
- (NSMutableDictionary *)cachedTarget
{
if (_cachedTarget == nil) {
_cachedTarget = [[NSMutableDictionary alloc] init];
}
return _cachedTarget;
} @end
2.每个组件创建一个Category,在其中实现当前组件的跳转实现
#import <CTMediator/CTMediator.h>
#import <UIKit/UIKit.h> @interface CTMediator (TAConfirmOrder) - (UIViewController *)confirmOrderViewControllerWithGoodsId:(NSString *)goodsId goodsName:(NSString *)goodsName ConfirmComplete:(dispatch_block_t)confirmComplete; @end #import "CTMediator+TAConfirmOrder.h" @implementation CTMediator (TAConfirmOrder) - (UIViewController *)confirmOrderViewControllerWithGoodsId:(NSString *)goodsId goodsName:(NSString *)goodsName ConfirmComplete:(dispatch_block_t)confirmComplete
{
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
params[@"goodsId"] = goodsId;
params[@"goodsName"] = goodsName;
params[@"completeBlock"] = confirmComplete;
return [self performTarget:@"TAConfirmOrder" action:@"ConfirmOrderViewController" params:params shouldCacheTarget:NO];
} @end
3.通过中间件调用
UIViewController *confirmOrderVC = [[CTMediator sharedInstance] confirmOrderViewControllerWithGoodsId:self.goodsId goodsName:self.goodsName ConfirmComplete:^{
//...
}];
iOS组件化方案的更多相关文章
- iOS 组件化方案
概述 近一年iOS业界讨论组件化方案甚多,大体来说有3种. Protocol注册方案 URL注册方案 Target-Action runtime调用方案 URL注册方案据我了解很多大公司都在采用,蘑菇 ...
- iOS应用架构谈 组件化方案
转载: iOS应用架构谈 组件化方案 简述 前几天的一个晚上在infoQ的微信群里,来自蘑菇街的Limboy做了一个分享,讲了蘑菇街的组件化之路.我不认为这条组件化之路蘑菇街走对了.分享后我私聊了Li ...
- iOS组件化实现方案
作者原文iOS组件化 - 路由架构从0到1实战 合伙呀 1.CTMediator作为路由中间件 2.基础UI组件以pod形式引入,并且能够独立运行调试 3.基础工具组件以pod形式引入,并且能够独立 ...
- iOS组件化思路 <转>
随着应用需求逐步迭代,应用的代码体积将会越来越大,为了更好的管理应用工程,我们开始借助CocoaPods版本管理工具对原有应用工程进行拆分.但是仅仅完成代码拆分还不足以解决业务之间的代码耦合,为了更好 ...
- iOS组件化思路-大神博客研读和思考
一.大神博客研读 随着应用需求逐步迭代,应用的代码体积将会越来越大,为了更好的管理应用工程,我们开始借助CocoaPods版本管理工具对原有应用工程进行拆分.但是仅仅完成代码拆分还不足以解决业务之间的 ...
- iOS 组件化 —— 路由设计思路分析
原文 前言 随着用户的需求越来越多,对App的用户体验也变的要求越来越高.为了更好的应对各种需求,开发人员从软件工程的角度,将App架构由原来简单的MVC变成MVVM,VIPER等复杂架构.更换适合业 ...
- iOS 组件化的几篇文章
随着工程的成长,开发人员的增多,合理的模块划分及低耦合的重要性显得愈发重要.最近在思考这方面的问题,也读了不少通过组件化解耦的文章,这里记录一下. 前 5 篇文章有些关联,建议阅读顺序,1.3.2.4 ...
- iOS 组件化路由框架 WisdomRouterKit 的应用
[前言] 大家好,写作是为了和读者沟通交流,欢迎各位开发者一起了解 WisdomRouterKit SDK 的功能. 关于 iOS 组件化路由方案框架: WisdomRouterKit 的功能介绍,之 ...
- Vue.js:轻量高效的前端组件化方案
转发一篇尤老师对vue.js的介绍,了解vue.js的来龙去脉.不过现在已经是2.0了,也有添加一些新的东西,当然有些东西也改了. Vue.js:轻量高效的前端组件化方案 Vue.js 是我在2014 ...
随机推荐
- 【LeetCode】029. Divide Two Integers
Divide two integers without using multiplication, division and mod operator. If it is overflow, retu ...
- C++语言对C的增强(2)—— const增强、枚举的增强
1.const基础知识 #include <iostream> int main(void) { //const定义常量--->const意味着只读 const int a; int ...
- 软件架构设计 ADMEMS方法体系
ADMEMS是Architecture Design Method has been Extended to Method System的简称,是由CSAI顾问团架构设计专家组于2009年11月在第六 ...
- linux NTP配置
时间是由计算机内的石英晶体振荡电路以:NetworkTimeProtocol(NTP):通常情况下,时间同步是按以下步骤进行的::(1):NTP客 户端向NTP服务器发出一个时间请:(2):当服务器接 ...
- bzoj 4559 [JLoi2016]成绩比较——拉格朗日插值
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=4559 关于拉格朗日插值,可以看这些博客: https://www.cnblogs.com/E ...
- 分区工具parted的详解及常用分区使用方法
一. parted的用途及说明 概括使用说明: parted用于对磁盘(或RAID磁盘)进行分区及管理,与fdisk分区工具相比,支持2TB以上的磁盘分区,并且允许调整分区的大小. ...
- 使用 DOM对象,控制HTML元素 来制作的一个简单的表格
制作一个表格,显示班级的学生信息. 要求: 1. 鼠标移到不同行上时背景色改为色值为 red,移开鼠标时则恢复为原背景色 white 2. 点击添加按钮,能动态在最后添加一行 3. 点击删除按钮,则删 ...
- CUDA Pro Tip: Optimized Filtering with Warp-Aggregated Atomics
In this post, I’ll introduce warp-aggregated atomics, a useful technique to improve performance when ...
- file“xxxxx”has modification times xxxxx s in the future..
这是因为一个项目从一个电脑拷贝的到另一个电脑上时,两个电脑的时钟不一致所致,修改一下项目所在目录的修改时间即可: find /your/dir -type f -exec touch {} + 也可以 ...
- POJ3641(快速幂)
Pseudoprime numbers Time Limit: 1000MS Memory Limit: 65536K Total Submissions: 8529 Accepted: 35 ...