casatwy / ctmediator Goto Github PK
View Code? Open in Web Editor NEWThe mediator with no regist process to split your iOS Project into multiple project.
License: Other
The mediator with no regist process to split your iOS Project into multiple project.
License: Other
如题
Thanks for your CTMediator, it is very impressive for me.
And there is a question coming into my mind, that is what does a URL represent?
In many apps, a URL, with host, path and action , stands for an individual page, which is commonly considered as a controller. Thus when we open a scheme, we will actually open a new page.
But in CTMediator, when we are trying to open a URL, we are actually calling a service method, without respect to what is serving backwards.
I'm wandering what will you do when you are trying to implement open url as page via CTMediator ?
如果action不接受参数的话,就时不时的崩溃掉。 设计中 action是一定要传参的吗?
结合自己的项目,加上您的这个框架,写了一套适合自己项目的Mediator,为了确保框架的稳定性,我接入了大量的单元测试,通过NSError **的方式能够将框架内部的error传出.有一个问题需要请教一下哈
1.在看了您的文章后在swift层,也准备接入,我打算在单元测试中测试您说的swift -> extension -> swift这八种情况,但是因为swift2.0后error的抛出只能使用throw
,那么如何能在swift的单元测试中检测到Objc抛出的error呢
2.您的代码链接在135行关于返回值的判断,其实是不足的,因为缺少很多种情况,走单元测试会导致崩溃,您是出于对项目返回值的一个规范吗
EXC_BAD_ACCESS
比如在调用一个接口成功后才返回这个viewcontroller
请问是我的代码有什么错误吗~一直没搞好,本来工程构建到一半卡住了
调用:override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) { let vc = CTMediator.sharedInstance().performTarget("T", action: "test", params: nil, shouldCacheTarget: false) navigationController!.pushViewController(vc! as! UIViewController, animated: true) }
target:class Target_T_: NSObject { func Action_test(params: [AnyHashable : Any]) -> UIViewController { return UIViewController() } }
An unexpected version directory Assets.xcassets
was encountered for the /Users/longmin/.cocoapods/repos/CTMediator/CTMediator
Pod in the CTMediator
repository.
casa老师,你好;
CTMediator类中方法有个疑问,target中有些方法是不需要参数的
performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget中
下面这两行代码就不是很合理了
NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
actionString = [NSString stringWithFormat:@"Action_%@WithParmas:",actionName];
我觉得“:” 写到Mediator的category中对actionName的声明中,
如 NSString * const kCTMediatorActionNativFetchDetailViewController = @"nativeFetchDetailViewController:";
这样是不是合理一些?
这样的话,在performTarget:(NSString *)targetName action:(NSString *)actionName params:(NSDictionary *)params shouldCacheTarget:(BOOL)shouldCacheTarget方法中,需要针对action是否是带参数进行区分是调用 [target performSelector:action]还是 [target performSelector:action withObject:params]
DemoModuleADetailViewController.m 文件中 41 和 44 行 size 是不是ct_size?
为什么mediator基类不能用纯swift实现?遇到什么困难了吗? @casatwy
你好,目前应用中有这样一种使用场景:
模块A中有个广告业务,广告有很多中接口,但最终都能转成一种view model
现在有模块B,它想通过路由使用A模块提供的广告功能,界面稍有不同,view model可以共用,那我如何通过target-action这种形式的路由实现这种场景呢?
@casatwy hi, there is an error occured when I using pod search CTMediator
and I have updated my pod libs. I don not know why. did I using it in wrong way?
您好,我想问一下模块与模块间如何调用呢?通过导入分类的方式?如果是这样的话,模块间会有耦合呐.
podfile添加source 'https://github.com/CocoaPods/Specs.git'
source 'https://github.com/ModulizationDemo/PrivatePods.git'
pod install 会自动建立私有库地址 少了前面一步
由于TableViewController并没有依赖于Cell,所以TableViewController无法直接成为cell的delegate。那么如果cell想要进行传值的话,那么只能由Target_A来成为cell的delegate,那Target_A应该怎样将值传递给TableViewController进行处理呢?(使用NSNotification貌似有点多余)
类之间delegate怎么处理?
背景:
项目语言swift,直接在项目文件下创建了业务模块,创建了target extension文件夹,kCTMediatorParamsKeySwiftTargetModuleName参数传入的是target所在的文件夹名字,使用的时候因为target为nil导致controller为nil,闪退。
有尝试过将demo中的A_swift文件夹放入demo项目中也出现闪退。
疑问:
swift项目中想使用CTMediator使用必须将业务模块做成cocoapods库才可以?
如果必须做成cocoapods库,那么开发的时候如何直接使用CTMediator?
#11 感觉有点类似这个问题
就是业务A中的事件如何传递到主工程中去响应?
业务A对应TargetA。业务A点击事件可以通过通知传递到TargetA中。但是业务A对应的CTMediator_A 如何在主工程中接收响应呢?
求指教
XXXX/Build/Intermediates.noindex/ZFMapModule.build/Debug-iphoneos/ZFMapModule_Example.build/Objects-normal/arm64/Target_MapModule.o
XXXX/Build/Products/Debug-iphoneos/ZFMapModule/ZFMapModule.framework/ZFMapModule(Target_MapModule.o)
(id)safePerformAction:(SEL)action target:(NSObject )target params:(NSDictionary )params
{
NSMethodSignature methodSig = [target methodSignatureForSelector:action];
if(methodSig == nil) {
return nil;
}
const char retType = [methodSig methodReturnType];
if (strcmp(retType, @encode(void)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
return nil;
}
if (strcmp(retType, @encode(NSInteger)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
NSInteger result = 0;
[invocation getReturnValue:&result];
return @(result);
}
if (strcmp(retType, @encode(BOOL)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
BOOL result = 0;
[invocation getReturnValue:&result];
return @(result);
}
if (strcmp(retType, @encode(CGFloat)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
CGFloat result = 0;
[invocation getReturnValue:&result];
return @(result);
}
if (strcmp(retType, @encode(NSUInteger)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
NSUInteger result = 0;
[invocation getReturnValue:&result];
return @(result);
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
}
下面的方法是不是更好点
(id)getReturnValueInvocatein:(NSInvocation *)invocation
{
const char *returnType = invocation.methodSignature.methodReturnType;
// Skip const type qualifier.
if (returnType[0] == 'r') {
returnType++;
}
if (strcmp(returnType, @encode(id)) == 0 || strcmp(returnType, @encode(Class)) == 0 || strcmp(returnType, @encode(void (^)(void))) == 0) {
__autoreleasing id returnObj;
[invocation getReturnValue:&returnObj];
return returnObj;
} else if (strcmp(returnType, @encode(char)) == 0) {
WRAP_GET_VALUE(char);
} else if (strcmp(returnType, @encode(int)) == 0) {
WRAP_GET_VALUE(int);
} else if (strcmp(returnType, @encode(short)) == 0) {
WRAP_GET_VALUE(short);
} else if (strcmp(returnType, @encode(long)) == 0) {
WRAP_GET_VALUE(long);
} else if (strcmp(returnType, @encode(long long)) == 0) {
WRAP_GET_VALUE(long long);
} else if (strcmp(returnType, @encode(unsigned char)) == 0) {
WRAP_GET_VALUE(unsigned char);
} else if (strcmp(returnType, @encode(unsigned int)) == 0) {
WRAP_GET_VALUE(unsigned int);
} else if (strcmp(returnType, @encode(unsigned short)) == 0) {
WRAP_GET_VALUE(unsigned short);
} else if (strcmp(returnType, @encode(unsigned long)) == 0) {
WRAP_GET_VALUE(unsigned long);
} else if (strcmp(returnType, @encode(unsigned long long)) == 0) {
WRAP_GET_VALUE(unsigned long long);
} else if (strcmp(returnType, @encode(float)) == 0) {
WRAP_GET_VALUE(float);
} else if (strcmp(returnType, @encode(double)) == 0) {
WRAP_GET_VALUE(double);
} else if (strcmp(returnType, @encode(BOOL)) == 0) {
WRAP_GET_VALUE(BOOL);
} else if (strcmp(returnType, @encode(char *)) == 0) {
WRAP_GET_VALUE(const char *);
} else if (strcmp(returnType, @encode(void)) == 0) {
return nil;
} else {
NSUInteger valueSize = 0;
NSGetSizeAndAlignment(returnType, &valueSize, NULL);
unsigned char valueBytes[valueSize];
[invocation getReturnValue:valueBytes];
return [NSValue valueWithBytes:valueBytes objCType:returnType];
}
return nil;
#undef WRAP_AND_RETURN
}
"CTMediator helps you to devide your project into multi-project", "devide" should be "divide"
为什么要在Mediator分类方法里面调用performTarget方法呢?直接new一个类返回不就行了?求解释.
参数,target都正常,已进入对应的SEL,执行完成之后,crash在此处:
您好,请教一下为什么参数是字典类型?换成 id 不是更好吗?实际上直接传id类型也是ok的,比如NSNumber、NSData。不知是否另有用意,望大佬赐教 @casatwy
// generate action
NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
这句代码是不是限制了被调用者的接口必须要带参数?因为actionString方法名后面的冒号是会一直存在的
(id)safePerformAction:(SEL)action target:(NSObject )target params:(NSDictionary )params
{
NSMethodSignature methodSig = [target methodSignatureForSelector:action];
if(methodSig == nil) {
return nil;
}
const char retType = [methodSig methodReturnType];
if (strcmp(retType, @encode(void)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
return nil;
}
if (strcmp(retType, @encode(NSInteger)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
NSInteger result = 0;
[invocation getReturnValue:&result];
return @(result);
}
if (strcmp(retType, @encode(BOOL)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
BOOL result = 0;
[invocation getReturnValue:&result];
return @(result);
}
if (strcmp(retType, @encode(CGFloat)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
CGFloat result = 0;
[invocation getReturnValue:&result];
return @(result);
}
if (strcmp(retType, @encode(NSUInteger)) == 0) {
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
[invocation setArgument:¶ms atIndex:2];
[invocation setSelector:action];
[invocation setTarget:target];
[invocation invoke];
NSUInteger result = 0;
[invocation getReturnValue:&result];
return @(result);
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
return [target performSelector:action withObject:params];
#pragma clang diagnostic pop
}
这个方法里面的校验是什么意思?有什么作用
目前正在研究您的组件化开发方式,但是遇到一个问题,就是我再businessA中用到的MediatorB, 在businessB中用到了MediatorA,然后我再demo中引入了这两个 businessA、businessB,工程就无法编译通过,麻烦大神帮忙指导下
businessA(依赖了MediatorB,通过分类调用了B业务的方法) 、businessB(依赖了MediatorA,通过分类调用了A业务的方法) 、MediatorA(依赖CTMediator)、MediatorB(依赖CTMediator) 都是单独的pod库
把 pod 'BusinessB', '> 0.4.0'> 0.7.0'
pod 'BusinessA', '
这两个一起引入后工程就报错:Apple Mach-O-Linker Error
单独使用就没错~~
在Swift工程中
NSString *targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
targetClass = NSClassFromString(targetClassString);
targetClass 为nil,后面应该在取一次targetClassString = [NSString stringWithFormat:@"工程名.Target_%@", targetName];
CTMediator
麻烦大神解答,非常感谢。
说明: 组件A和组件B如何互相调用, 尽量不要考虑组件都包含CTMediator的情况。因为这样的话,写一个头文件协议是不是更简单?
另外,组件A和组件B只是方便说明,这里要考虑的情况就是有很多个组件,至少具体多少个也只是程序运行后才知道的。
Class targetClass = NSClassFromString(targetClassString);
id target = [[targetClass alloc] init];
SEL action = NSSelectorFromString(actionString);
这样不是每perform一次都会新生成一个target,调多了内存怎么管理
尊敬的作者你好,
我想问一下,如果平台有类似于userManger这种单例对象,各业务线想要获取该对象应该怎么处理?
该对象拥有一些user属性,另外还会存在多种类似于register,clear的方法,业务方可能需要调用[UserManger shareManager].userName或[[UserManger shareManager] register]。
其中方法可以category映射出来,属性也可以写一个get方法通过字典传出来。
我想请问的是,在实际使用中有没有更好的方法?
一个业务组件对应一个pod target的入口?为什么不将所有的target 入口放在同一个pod 里面呢?
“直接把整个model层组件化后暴露给所有组件,容易造成数据泄露,付出的代价有点大”
这句话我没理解造成数据泄露是什么意思,可否举个例子
付出的代价是什么?可否举个例子
发现升级到swift 4.0之后,重新更新后引入了CTHandyCategories
,查看源码就是处理无响应请求的时候弹出一个alertview。对于稍微有点代码洁癖的码农来说,不希望引入额外的代码文件~
#####建议让用户自己处理无响应的情况,改进如下:
typedef id(^NoTargetActionHandler)(NSString *targetName, NSString *actionName)
@interface CTMediator : NSObject
@property (nonatomic, copy) NoTargetActionHandler handler;
@end
然后使用handler
替换掉[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString];
,这样可以去掉对CTHandyCategories
,同时也可以让用户自己处理无响应的情况。
在AViewController.m,viewDidLoad中设置self.view.backgroundColor = [UIColor yellowColor];后,run。界面没有任何变化。
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
} else {
// 这里是处理无响应请求的地方,如果无响应,则尝试调用对应target的notFound方法统一处理
SEL action = NSSelectorFromString(@"notFound:");
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
} else {
// 这里也是处理无响应请求的地方,在notFound都没有的时候,这个demo是直接return了。实际开发过程中,可以用前面提到的固定的target顶上的。
[self NoTargetActionResponseWithTargetString:targetClassString selectorString:actionString originParams:params];
[self.cachedTarget removeObjectForKey:targetClassString];
return nil;
}
}
此处判断走的是else,不知道是什么原因,之前swift4.0的时候代码是可以运行的。
pod install 之后, 项目跑不起来, 提示:
ld: warning: directory not found for option '-L/Users/greatman/Library/Developer/Xcode/DerivedData/CTMediator-bmljppouvvqrztaquakdqpyyykij/Build/Products/Debug-iphonesimulator/HandyFrame'
ld: library not found for -lHandyFrame
clang: error: linker command failed with exit code 1 (use -v to see invocation)
好像是没有lHandyFrame这个东西...
大神你好,看了你的iOS组件化方案,受益颇深,目前我也在对公司的项目进行组件化重构,现在遇到这个问题:
我觉得项目中的一些通用UI也应该作为一个组件存在,那么在使用UI组件的过程中,就免不了在对组件实例化之后进行赋值等操作,比如UITableViewCell,必然要在复用时进行赋值操作,而复用就意味着cell实例已经存在。在您的方案中,只能在获取实例的同时对实例进行操作,对已存在的实例却无能为力,我想请教下您针对这种情况有没有什么好的方案,谢谢
我没有用私有pod去分离组件,而是每个组件对应一个单独的project放在workspace里。很好奇为什么这样在其他类里执行的时候会初始化target不成功,targetClass地址也没有是0X0.只有当我把子组件里的对应的Target_XX.h暴露在需要调用的其他组件,并且初始化一个对象的时候才能在您的库里初始化成功。而且试了试我如果只导入这个.h,没有初始化也无法初始化成功,必须得和下图一样手动调一下才行。是我有什么操作不当的地方吗
`- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
Target_Index *target = [Target_Index new];`
UIViewController *vc = [[CTMediator sharedInstance] index_viewControllerWithTitle:@"测试"];
}
NSMethodSignature* methodSig = [target methodSignatureForSelector:action];
第一次:在 tagetA 中创建一个方法A 组件化调用 methodSig 正常获取,
第二次:修改tagetA中方法 A的名字,修改 action 字符串名称之后,运行 methodSig返回 nil,重启 xcode之后 methodSig可以正常获取。
环境 xcode10.2
[[CTMediator sharedInstance] CTMediator_showAlertWithMessage:@"casa" cancelAction:nil confirmAction:^(NSDictionary *info) {
// 做你想做的事
}];
- (void)viewWillAppear:(BOOL)animated
{
[super viewWillAppear:animated];
[self.tableView fill];
}
这样写是否才好?
- (void)viewWillLayoutSubviews
{
[self.tableView fill];
}
Hi,Casa。safePerformAction: target: 里面只对常用的数据类型做了判断,然后其他的类型统一使用perform selector完成。为什么不是所有的操作都直接使用perform selector,而要单独对一部分类型做判断~
您好,主要是方法那里不能识别出来。
// 有可能target是Swift对象
actionString = [NSString stringWithFormat:@"Action_%@WithParams:", actionName];
action = NSSelectorFromString(actionString);
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
} else {
// 这里是处理无响应请求的地方,如果无响应,则尝试调用对应target的notFound方法统一处理
SEL action = NSSelectorFromString(@"notFound:");
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
} else {
// 这里也是处理无响应请求的地方,在notFound都没有的时候,这个demo是直接return了。实际开发过程中,可以用前面提到的固定的target顶上的。
[self.cachedTarget removeObjectForKey:targetClassString];
return nil;
}
}
上面这一断,进入到了else里面。
我的demo代码中,Target_Register.swift文件代码出下:
Target中代码如下:
@objc(Target_Register)
class Target_Register: NSObject {
func Action_registerViewController(params: [String:Any]) -> XYJBaseVC {
let vc = XYJRegisterVC()
return vc
}
}
category中代码如下:
// 1. 字符串 是类名 Target_xxx.h 中的 xxx 部分
let kCTMediatorTarget_Register = "Register"
// 2. 字符串是 Target_xxx.h 中 定义的 Action_xxxx 函数名的 xxx 部分
let kCTMediatorActionNavigaTo_RegisterVC = "registerViewController"
extension CTMediator {
func ylx_mediator_registerVCWithParams(params: [String:Any]) -> XYJBaseVC {
if let registerVc = performTarget(kCTMediatorTarget_Register, action: kCTMediatorActionNavigaTo_RegisterVC, params: params, shouldCacheTarget: false) as? XYJRegisterVC {
return registerVc
} else {
// 这里处理异常场景,具体如何处理取决于产品
return XYJBaseVC.init()
}
}
}
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.