先来看看 NSMethodSignature 和 NSInvocation
假设现在有一个方法
-(NSString *)school:(NSString *)name time:(NSInteger)t{
NSString *result = [NSString stringWithFormat:@"%@%d",name,t];
NSLog(@"---result-----%@", result);
return result;
}
常规掉用我们都知道 现在我们使用 NSMethodSignature 和 NSInvocation 怎么玩
NSMethodSignature *signature = [self methodSignatureForSelector:@selector(schoolNmae:time:)];
NSMethodSignature 获得方法签名 有两种方式
- methodSignatureForSelector 通过类掉用则是获得类方法 的 签名 通过 类对象掉用 则是获得实例方法的签名
- instanceMethodSignatureForSelector 只能获得实例方法的签名 并且只能用类去掉用
NSMethodSignature 有几个属性 分别为
- methodReturnType 返回值类型 v(无返回值) @(对象) i(int) f(float)
- methodReturnLength 大于0 代表有返回值
- numberOfArguments 参数个数 这里 schoolNmae 方法是4个参数 为什么? 我们使用 clang -rewrite-objc 文件名 将方法转成C语言源代码 可以发现 方法变成了 下面这样
static NSString * _I_MyTest_school_time_(MyTest * self, SEL _cmd, NSString *name, NSInteger t) {
NSString *result = ((NSString *(*)(id, SEL, NSString *, ...))(void *)objc_msgSend)((id)objc_getClass("NSString"), sel_registerName("stringWithFormat:"), (NSString *)&__NSConstantStringImpl__var_folders_5l_l354rhkd3sl86kc7m8d8nwrr0000gn_T_MyTest_92ee17_mi_2, (NSString *)name, (NSInteger)t);
return result;
}
可以看到 多了 MyTest * self, SEL _cmd
这两个 参数 加上原本的 共4个
*** NSInvocation 使用 ***
设置方法
[invocation setSelector:@selector(school:time:)];
设置参数 在第二个位置
[invocation setArgument:&name atIndex:2];
设置参数 在第三个位置
[invocation setArgument:&t atIndex:3];
设置掉用对象
[invocation setTarget:self];
参数引用计数 加1
[invocation retainArguments];
掉用方法
[invocation invoke];
NSString *result = @"";
获取最后的返回值
[invocation getReturnValue:&result];
注意上面获取返回值是有问题的 程序直接崩溃 为什么 ?
因为在arc 模式下 NSString *result = @""; 这样的声名 result 是__strong 的 但是 getReturnValue 只是把值拷贝到指定的内存地址 引用计数并没有加一 也就是说result没有获得返回值的强引用 当返回值出了作用域 会被释放 同时因为我们的 result 是__strong的 所以arc会误认为我们是强引用过了 所以 result 的声名 改成 __unsafe_unretained NSString *result = @""; 就好了 arc会给我们处理
再来看看CTMediator
- 使用方法
[[CTMediator sharedInstance] performTarget:@"A" action:@"GetName" params:@{@"key":@"value"} shouldCacheTarget:YES];
performTarget
target 是我们要掉用的类 action是我们要掉用的方法 params 是我们要传入的参数 CTMediator 的 performTarget 方法 比较长这里只列出几行代码
拿到我们要掉用的类 注意只能 以 Target_ 开头的
targetClassString = [NSString stringWithFormat:@"Target_%@", targetName];
从缓存中拿到我们的目标类对象 如果没有就创建一个
NSObject *target = self.cachedTarget[targetClassString];
if (target == nil) {
Class targetClass = NSClassFromString(targetClassString);
target = [[targetClass alloc] init];
}
拿到目标方法 注意只接受 Action_ 开头的方法
NSString *actionString = [NSString stringWithFormat:@"Action_%@:", actionName];
SEL action = NSSelectorFromString(actionString);
if (shouldCacheTarget) {
self.cachedTarget[targetClassString] = target;
}
如果方法相应 执行
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target params:params];
}
safePerformAction
仍然我们只粘贴其中一段代码
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;
}
这里就用到了我们最开始讲的内容不再赘述 至此一个方法掉用完毕 当然还有一些异常处理 这里不再将 看源代码 一看就懂
总结
今天写这个用什么用呢 其实我在工作中没有直接用这种方法调用过方法,前几天看了一种设计模式 中介者模式 这种模式我个人觉得是用来协调组件之间掉用逻辑的模式 组件内部我们可以使用MVVM MVC 等。在掉用组件的时候 为这个组件创建一个CTMediator的类别 所有跟这个组件之间的交互 都只依赖这个中介者 实现了组件间的解偶合