我的女神——简洁实用的iOS代码调试框架

简介:

我的女神——简洁实用的iOS代码调试框架

一、引言

        这篇博客的起源是接手了公司的一个已经完成的项目,来做代码优化,项目工程很大,并且引入了很多公司内部的SDK,要搞清楚公司内部的这套框架,的确不是件容易的事,并且由于这个项目是多人开发的,在调试阶段会打印出巨量的调试信息,使得浏览有用信息变的十分困难,更加恐怖的是,很多信息是SDK中的调试打印,将这些都进行注销是非常费劲甚至不可能的事,于是便有了这样一些需求:首先,我需要清楚了解各个controller之间的跳转关系,需要快速的弄清每个stroyBoard中各个controller的来龙去脉,其次,我想在不改变其他人的调试代码的情况下,屏蔽冗余的log信息,让我的调试数据更加清晰明了。于是我想到了如下的解决方案,同样,如果你有更好的方案或者你知道的优秀的解决办法,请告知我,十分感谢。

二、追踪程序的跳转路径

        这是一个很容易解决的问题,我们都知道,一个controller,如果要展现出来,一定会走生命周期中的viewWillAppear这个方法,我们只需要在这个方法中做些手脚就可以了,实现有两种思路,一种是采用工厂的设计模式,建立工厂类Controller,在其viewWillAppear中加入我们的调试代码,但这对于我的项目并不实用,首先我不确定所有controller都会继承于一个父类,其次,在我没有找到源头时,这些类已经在公司的framework中了,我根本没办法操作源码。而第二种方案就是runtime,对的,运行时的OC,没有不可能。关于runtime的详细说明,在http://my.oschina.net/u/2340880/blog/489072中有介绍。思路是我们可以写一个方法,替换掉系统的viewWillAppear,在其中加入我们的调试代码,这个方法就是Method Swizzing,代码设计如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
//新建一个conreoller的类别
#import "UIViewController+YHBaseTest.h"
#import <objc/runtime.h>
@implementation UIViewController (YHBaseTest)
+ ( void )load {
     //只执行一次的线程
     static  dispatch_once_t onceToken;
     dispatch_once(&onceToken, ^{
         Class  class  = [self  class ];
         // When swizzling a class method, use the following:
         // Class class = object_getClass((id)self);
         //创建两个选择器 分别指向 系统的和我们要替换的函数
         SEL originalSelector = @selector(viewWillAppear:);
         SEL swizzledSelector = @selector(YHBaseViewWillAppear:);
         //获取方法实例
         Method originalMethod = class_getInstanceMethod( class , originalSelector);
         Method swizzledMethod = class_getInstanceMethod( class , swizzledSelector);
         //现将方法加入
         BOOL  didAddMethod =
         class_addMethod( class ,
                         originalSelector,
                         method_getImplementation(swizzledMethod),
                         method_getTypeEncoding(swizzledMethod));
         //进行方法替换
         if  (didAddMethod) {
             class_replaceMethod( class ,
                                 swizzledSelector,
                                 method_getImplementation(originalMethod),
                                 method_getTypeEncoding(originalMethod));
         else  {
             method_exchangeImplementations(originalMethod, swizzledMethod);
         }
     });
}
 
#pragma mark - Method Swizzling
 
- ( void )YHBaseViewWillAppear:( BOOL )animated {
//这里是是我加的一个控制调试锁 后面会介绍
     [self YHBaseViewWillAppear:animated];
     YHBaseProcessLog(@ "YHBaseTest:ViewWillAppear: %@" , self);
     
}

三、屏蔽冗余的log信息

1、系统的NSLog是个什么玩意

        要战胜我们的敌人,首先应该了解我们的敌人,我们想要屏蔽NSLog的打印,先需要清楚NSLog到底是个什么玩意。

        首先,NSLog的定义如下:

?
1
2
FOUNDATION_EXPORT  void  NSLog(NSString *format, ...) NS_FORMAT_FUNCTION(1,2);
FOUNDATION_EXPORT  void  NSLogv(NSString *format,  va_list  args) NS_FORMAT_FUNCTION(1,0);

这里面有两个函数,一个是使用多参的格式化字符串进行NSLog的打印,一个是通过参数指针进行打印。从这里我们可以看出,系统的NSLog是一个C风格的函数,所以,我们有思路了,我们可以通过定义一个NSLog宏来替换掉项目中所有的NSLog,如下:

?
1
2
//...是省略参数的宏的写法,后面的__VA_ARGS__是系统定义好的一个宏,来声明不定参数
#define NSLog(...) YHBaseTestLog(__VA_ARGS__)

2、实现我们自己的NSLog

        通过上面的方法,在没有动源码的情况下,我们已经可以替换掉程序中所有的打印,可能你会疑问,程序中怎么会允许我们有两个NSLog呢,其实这没什么神奇的,要知道宏是一种预编译的指令,所有这些操作是在代码编译之前完成的,实际上程序中已经将NSLog简单替换成了我们的函数调用,程序中只有一个NSLog,这就是宏的强大之处,狸猫换太子,不错吧。

        下面我们来实现我们的这个函数,如下:

?
1
2
3
4
5
6
7
8
9
10
11
12
//不要忘了在.h文件中声明
void  YHBaseTestLog(NSString *str,...){
//参数列
     va_list  list;
     va_start (list, str);
     //这个地方是一个锁,后面会介绍
     if  (![YHBaseTestLock sharedTheSingletion]->_customLock) {
     //进行打印
            NSLogv(str, list);
     }
     va_end (list);
}

这个函数中其实并没有做什么,加了一个锁的判断,仅此而已,核心的控制,就交给我们的锁吧:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
//.h文件
@interface YHBaseTestLock : NSObject<YHSingletonProcotol>
{
     @ public
     BOOL  _customLock;
     BOOL  _precessLock;
}
+( void )customLock;
+( void )customUnLock;
 
+( void )processLogLock;
+( void )processLogUnLock;
 
//.m
//单例方法
+(instancetype)sharedTheSingletion{
     static  YHBaseTestLock * sharedModel = nil;
     static  dispatch_once_t predicate;
     dispatch_once(&predicate, ^{
         sharedModel = [[YHBaseTestLock alloc] init];
     });
     return  sharedModel;
}
- (instancetype)init
{
     self = [super init];
     if  (self) {
     //初始化 默认用户的打印都开起
         _customLock=NO;
         //默认 我们加的controller的打印屏蔽
         _precessLock=YES;
     }
     return  self;
}
//响应的设置
+( void )customLock{
     [YHBaseTestLock sharedTheSingletion]->_customLock=YES;
}
+( void )customUnLock{
       [YHBaseTestLock sharedTheSingletion]->_customLock=NO;
}
 
+( void )processLogLock{
      [YHBaseTestLock sharedTheSingletion]->_precessLock=YES;
}
+( void )processLogUnLock{
     [YHBaseTestLock sharedTheSingletion]->_precessLock=NO;
}

四、看看我们的杰作吧

        做完上面的工作后,我们在appdelegate中如如下的简单配置:

?
1
2
3
4
5
6
7
8
- ( BOOL )application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
     // Override point for customization after application launch.
     //将用户打印加锁
     [YHBaseTestLock customLock];
     //将流程打印解锁
     [YHBaseTestLock processLogUnLock];
     return  YES;
}

我们做如下测试:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
@interface ViewController ()
 
@end
 
@implementation ViewController
- ( void )viewDidLoad {
     [super viewDidLoad];
     // Do any additional setup after loading the view, typically from a nib.
    NSLog(@ "111111111111111111111111111111111111111111111111111111111111111" );
     [YHBaseTestLock customUnLock];
     NSLog(@ "这个是有用的信息:%@==%d" ,@ "看我哦" ,__LINE__);
     [YHBaseTestLock customLock];
     NSLog(@ "32123213123214412312312" );
}

我在viewController中的有用信息前后,打印了一些干扰信息,并且可以看到,这个NSLog的格式和系统的完全兼容,在打印有用信息前后解锁和加锁,结果如下:

214659_H6RY_2340880.png

可以看到,我们将没用的打印都屏蔽了,并且打印了程序的跳转流程。最重要的是,我们对源码一个字符都没有修改,同时不会影响与冲突其他人的开发。


这篇博客开头,我称之为我的女神,真心实感,这个方法帮了我很大的忙,在我了解项目的结构框架前,每次调试打印都在控制区滚出一片片的信息着实让我头晕脑胀,现在一清凉,精神也清爽不少,^_^。


目录
相关文章
|
1月前
|
移动开发 安全 数据安全/隐私保护
iOS 全局自动化代码混淆工具!支持 cocoapod 组件代码一并混淆
iOS 全局自动化代码混淆工具!支持 cocoapod 组件代码一并混淆
|
3月前
|
机器学习/深度学习 PyTorch TensorFlow
是否有其他框架可以在iOS设备上进行机器学习?
是否有其他框架可以在iOS设备上进行机器学习?
21 1
|
3月前
|
存储 数据建模 iOS开发
iOS设备功能和框架: 什么是 Core Data,它在 iOS 中的作用是什么?
iOS设备功能和框架: 什么是 Core Data,它在 iOS 中的作用是什么?
32 1
|
3月前
|
定位技术 iOS开发
iOS设备功能和框架: 如何使用 Core Location 获取设备的位置信息?
iOS设备功能和框架: 如何使用 Core Location 获取设备的位置信息?
19 0
|
3月前
|
移动开发 前端开发 安全
最强大的 iOS 应用源码保护工具:Ipa Guard,保护你的商业机密代码
最强大的 iOS 应用源码保护工具:Ipa Guard,保护你的商业机密代码
|
3月前
|
移动开发 前端开发 数据安全/隐私保护
【工具】iOS代码混淆工具-iOS源码混淆
【工具】iOS代码混淆工具-iOS源码混淆
42 1
|
2月前
|
移动开发 安全 数据安全/隐私保护
iOS 代码混淆和加固技术详解
iOS 代码混淆和加固技术详解
|
2月前
|
移动开发 前端开发 数据安全/隐私保护
iOS 代码混淆 - 从入门到放弃
iOS 代码混淆 - 从入门到放弃
|
2月前
|
安全 算法 数据安全/隐私保护
iOS 代码加固与保护方法详解 - 提升 iOS 应用安全性的关键步骤
iOS 代码加固与保护方法详解 - 提升 iOS 应用安全性的关键步骤
|
3月前
|
机器学习/深度学习 PyTorch TensorFlow
iOS设备功能和框架: 什么是 Core ML?如何在应用中集成机器学习模型?
iOS设备功能和框架: 什么是 Core ML?如何在应用中集成机器学习模型?
27 0