準備工作
// 返回值id
// 外部調用, 通過target和action來唯一確認一個類里面的方法
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName param:(NSDictionary *)para;
-
說明
- target-action 兩個參數來確定target 和 action , para 你需要傳遞的參數
接口實現
- (id)performTarget:(NSString *)targetName action:(NSString *)actionName param:(NSDictionary *)para {
// 這個目標的類名字符串
NSString *targetClassString = [NSString stringWithFormat:@"RYLSJTA_%@",targetName];
NSString *actionMethondString = [NSString stringWithFormat:@"action_%@:",actionName];
Class targetClass = NSClassFromString(targetClassString);
NSObject *target = [[targetClass alloc] init];
SEL action = NSSelectorFromString(actionMethondString);
// 判斷
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target param:para];
} else {
SEL action = NSSelectorFromString(@"notFound:");
if ([target respondsToSelector:action]) {
return [self safePerformAction:action target:target param:para];
} else {
return nil;
}
}
return nil;
}
-
說明
- 返回值使用ID類型是因為我們可能在模塊中得到的是一個登錄的控制器,而不是其它類型。
- 如果沒有需要的類或者實現的方法我們需要做額外的處理,防止崩潰
- 如果你有參數傳遞需要處理的話 需要在 action_%@: 這里加上個冒號不然沒有反應
傳遞的參數處理(核心代碼)
// 1.通過對象調用指定的方法
// 2.傳參
- (id)safePerformAction:(SEL)action target:(NSObject *)target param:(NSDictionary *)para {
NSMethodSignature *methodSig = [target methodSignatureForSelector:action];
if (methodSig == nil) {
return nil;
}
// 方法簽名的返回值
const char *retType = [methodSig methodReturnType];
// id 是可以返回任意對象 所以 我們單獨處理基本變量. NSInteger Bool Void...
if (strcmp(retType, @encode(NSInteger)) == 0) {
//通過方法調用者創建方法簽名;此方法是NSObject 的方法
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
// 為什么傳2? 前面0和1這兩個位置已經被target和action給占用了.
[invocation setArgument:¶ atIndex:2];
[invocation setTarget:target];
[invocation setSelector:action];
// 執行invocation
[invocation invoke];
NSInteger result = 0;
[invocation getReturnValue:&result];
return @(result);
}
if (strcmp(retType, @encode(BOOL)) == 0) {
// 通過方法調用者創建方法簽名;此方法是NSObject 的方法
NSInvocation *invocation = [NSInvocation invocationWithMethodSignature:methodSig];
// 為什么傳2? 前面0和1這兩個位置已經被target和action給占用了.
[invocation setArgument:¶ atIndex:2];
[invocation setTarget:target];
[invocation setSelector:action];
// 執行invocation
[invocation invoke];
NSInteger result = 0;
[invocation getReturnValue:&result];
return @(result);
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
return [target performSelector:action withObject:target withObject:para];
#pragma clang diagnostic pop
}
-
說明
- setArgument 設置參數一定要從2開始,因為里面模式是self和_cmd 2個給占用了
- 如果有多個參數的話 就atIndex:3,atIndex:4 這種形式就可以
- strcmp(retType, @encode(BOOL) 轉碼判斷,這里的類型可以根據情況設置
- 使用push pop 是為了消除警告,因為我們在上面已經處理過了,我們已經知道了類型,所以這里消除警告即可
-
實現效果
最終開發想要的效果
-
測試效果
Router Demo 測試結果展示
-
Demo分享