我的女神——简洁实用的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,代码设计如下:

//新建一个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的定义如下:

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,如下:

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

2、实现我们自己的NSLog

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

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

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

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

//.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中如如下的简单配置:

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

我们做如下测试:

@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-05 19:08:16

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

实用的IOS应用程序框架

目录 概述 概述

[编写高质量iOS代码的52个有效方法](十一)系统框架

[编写高质量iOS代码的52个有效方法](十一)系统框架 参考书籍:<Effective Objective-C 2.0> [英] Matt Galloway 先睹为快 47.熟悉系统框架 48.多用块枚举,少用for循环 49.对自定义其内存管理语义的容器使用无缝桥接 50.构建缓存时选用NSCache而非NSDictionary 51.精简initialize与load的实现代码 52.别忘了NSTimer会保留其目标对象 目录 编写高质量iOS代码的52个有效方法十一系统框架 先睹为快

ios开发——实用技术篇OC篇&amp;iOS的主要框架

iOS的主要框架         阅读目录 Foundation框架为所有的应用程序提供基本系统服务 UIKit框架提供创建基于触摸用户界面的类 Core Data框架管着理应用程序数据模型 Core Graphics框架帮助你创建图形 Core Animation允许你创建高级的动画和虚拟效果 OpenGL ES 框架提供2D和3D绘图工具 将别的框架添加到工程里 本文是<Sunvey the Major Framworks>一文的翻译 框架是一个目录,这个目录包含了共享库,访问共享库里代码

使用Playground来调试ios代码

1.Playground介绍 Playground是苹果xcode6自带的一种快速学习代码的模式,这种模式不用建立工程,直接就能看到代码执行结果,对于快速原型验证,以及学习语言细节等方面,有着独特优势.缺点是不能有用户交互输入. 我看到大家使用的例子,都是写的用osx的代码和类库来测试(默认使用快捷菜单创建的也是mac osx版本的Playground).看了苹果的说明,Playground是支持ios的. 于是就找找看,怎么样支持ios的类库. 2.创建Playground 首先xcode启动

编写高质量的iOS代码--Effective Objective-C 2.0 读书笔记

编写高质量的iOS代码--Effective Objective-C 2.0 读书笔记 这本书年初刷完,感觉不错,介绍了很多小点,都是平日不怎么关注的. 第1章 熟悉Objective-C 这章没什么好介绍 第1条:了解Objective-C语言的起源 第2条:在类的头文件中尽量少引入其他头文件 第3条:多用字面量语法,少用与之等价的方法 第4条:多用类型常量,少用#define预处理指令 要理解为啥要少用#define预处理指令. 然后具体用哪个, 自己定吧 第5条:用枚举表示状态.选项.状态

[iOS]数据库第三方框架FMDB详细讲解

[iOS]数据库第三方框架FMDB详细讲解 初识FMDB iOS中原生的SQLite API在进行数据存储的时候,需要使用C语言中的函数,操作比较麻烦.于是,就出现了一系列将SQLite API进行封装的库,例如FMDB.PlausibleDatabase.sqlitepersistentobjects等. FMDB是一款简洁.易用的封装库.因此,在这里推荐使用第三方框架FMDB,它是对libsqlite3框架的封装,用起来的步骤与SQLite使用类似,并且它对于多线程的并发操作进行了处理,所以

iOS各种调试技巧豪华套餐

转载自http://www.cnblogs.com/daiweilai/p/4421340.html 目录 前言 逼优鸡 知己知彼 百战不殆 抽刀断Bug 普通操作 全局断点(Global BreakPoint) 条件断点(Condational Breakpoints) 打印的艺术 NSLog 开启僵尸对象(Enable NSZombie Objects) 进击的码农 Console(lldb 命令) Profile(instruments) Xcode视图调试 结语 前言 最近博主临近毕业季

漫谈iOS Crash收集框架

漫谈iOS Crash收集框架 为了能够第一时间发现程序问题,应用程序需要实现自己的崩溃日志收集服务,成熟的开源项目很多,如 KSCrash,plcrashreporter,CrashKit 等.追求方便省心,对于保密性要求不高的程序来说,也可以选择各种一条龙Crash统计产品,如 Crashlytics,Hockeyapp ,友盟,Bugly 等等. 是否集成越多的Crash日志收集服务就越保险? 自己收集的Crash日志和系统生成的Crash日志有分歧,应该相信谁? 为什么有大量Crash日

IOS各种调试

IOS各种调试技巧豪华套餐 目录 前言逼优鸡知己知彼 百战不殆抽刀断Bug 普通操作 全局断点(Global BreakPoint) 条件断点(Condational Breakpoints)打印的艺术 NSLog 开启僵尸对象(Enable NSZombie Objects)进击的码农 Console(lldb 命令) Profile(instruments) Xcode视图调试结语 前言 最近博主临近毕业季,为了完美的写一篇毕业论文,真是:“锄禾日当午,汗滴禾下土”<—— 这句诗跟毕业我写毕