我的女神——简洁实用的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的格式和系统的完全兼容,在打印有用信息前后解锁和加锁,结果如下:

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

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

时间: 2024-08-04 07:34:06

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

配合LLDB调试器进行iOS代码调试

配合LLDB调试器进行iOS代码调试         在一款完整iOS移动应用的开发中,代码的调试和编写占着同等重要的地位.Xcode默认使用LLDB作为代码调试器,LLDB功能丰富且强大,恰当的使用它,可以帮助开发者事半功倍的完成代码调试的工作. 1.expression代码执行指令         关于LLDB调试器,最常用的指令应该是p与po了,开发者常用这两个命令来进行对象的打印操作,p会打印出对象地址和类型,po则会额外打印出对象的值得内容,实际上,这两个命令都是expression相

ios-AppCan中 iOS原生代码 调试插件 中出现的问题

问题描述 AppCan中 iOS原生代码 调试插件 中出现的问题 在调试插件过程中 index.html中的 按钮可以调用createDemo(); 但是createDemo();中得uexDemo.open是怎么回调的? function createDemo(){ alert("----"); uexDemo.open('0','120','320','416'); } uexDemo在plugin.xml中写了 <?xml version="1.0" e

使用iOS原生sqlite3框架对sqlite数据库进行操作

使用iOS原生sqlite3框架对sqlite数据库进行操作 一.引言       sqlite数据库是一种小型数据库,由于其小巧与简洁,在移动开发领域应用深广,sqlite数据库有一套完备的sqlite语句进行管理操作,一些常用的语句和可视化的开发工具在上篇博客中有介绍,地址如下: sqlite数据库常用语句及可视化工具介绍:http://my.oschina.net/u/2340880/blog/600820.       在iOS的原生开发框架中可以对sqlite数据库进行很好的支持,这个

网页教学网代码分享:干净简洁的网页列表代码

文章简介:网页教学网代码分享:干净简洁的网页列表代码.  谁不希望有一个好看而又干净的网页列表?这篇文章中我们给出几个实用的例子,你可以把他们直接用到自己的工作中. 我们从一个带有动画效果的垂直列表开始,接着是一个图文混排的例子,然后是一个只有图片的list例子跟一个水平菜单的例子,最后是一个以放大数字开头的列表.对于初学者而言这里可以学到很多东西,对于熟手,直接拿来用就是了. 1.helvetica字体的列表 第一个例子的样式看起平淡无奇,不过喜欢简约风格的人也许会感兴趣,这类似于印刷字体风格

CLR 调试接口的架构与应用 [2] 调试框架

架构 如 Don Box 在<.NET本质论 第1卷:公共语言运行库>一书的第10章中介绍, CLR 调试框架是一个由 CLR 提供的,面向工具开发商的,支持调试功能的最小功能集.与 JVM 的 JDI (Java Debug Interface)不同,CLR 调试框架不仅仅关注于虚拟机一级的调试,同时也提供了 Native 一级调试的统一接口.使得现有工具开发商能够以最小代价移植并支持 CLR 调试功能.而对 CLR 调试更高层次或更细粒度的支持,则是由前面提到的 Profiling API

iOS崩溃调试

在iOS开发调试过程中以及上线之后,程序经常会出现崩溃的问题.简单的崩溃还好说,复杂的崩溃就需要我们通过解析Crash文件来分析了,解析Crash文件在iOS开发中是比较常见的. 现在网上有很多关于解析崩溃信息的博客,但是大多质量参差不齐,或者有些细节没有注意到.今天写一篇博客总结一下我对崩溃调试的使用和技巧,如果有哪些错误或遗漏,还请指点,谢谢! 获取崩溃信息 在iOS中获取崩溃信息的方式有很多,比较常见的是使用友盟.百度等第三方分析工具,或者自己收集崩溃信息并上传公司服务器.下面列举一些我们

iOS崩溃调试的使用和技巧总结

 每日更新关注:http://weibo.com/hanjunqiang  新浪微博 在iOS开发调试过程中以及上线之后,程序经常会出现崩溃的问题.简单的崩溃还好说,复杂的崩溃就需要我们通过解析Crash文件来分析了,解析Crash文件在iOS开发中是比较常见的. 现在网上有很多关于解析崩溃信息的博客,但是大多质量参差不齐,或者有些细节没有注意到.今天写一篇博客总结一下我对崩溃调试的使用和技巧,如果有哪些错误或遗漏,还请指点,谢谢! 获取崩溃信息 在iOS中获取崩溃信息的方式有很多,比较常见的是

iOS高效调试

写代码难免出现bug. 储备些调试技能绝对能够提高你的工作效率,让bug无所遁形.下面就和大家分享一些我在工作中常用的iOS调试小技能. 1. 打印 最简单,基础的调试方法就是打印日志了.贴出两段封装好的日志打印代码: //swift版  func DLog(message: T, file: String = #file, method: String = #function, line: Int = #line) {      #if DEBUG          print(", \(me

关于第三方IOS的checkBox框架的使用

关于第三方IOS的checkBox框架的使用 这个框架是从github上下载获取的:M13Checkbox. 只是github的源码项目工程比较久远,所以我把代码部分拷贝到XCode 7.1.0新建的项目里. 下面是展示效果   客户端源码使用参考: 1 #import "ViewController.h" 2 #import "M13Checkbox.h" 3 4 @interface ViewController () 5 6 @end 7 8 @impleme