文档章节

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

珲少
 珲少
发布于 2015/09/10 21:56
字数 1675
阅读 652
收藏 12

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

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

 

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

 

专注技术,热爱生活,交流技术,也做朋友。

——珲少 QQ群:203317592

© 著作权归作者所有

珲少

珲少

粉丝 881
博文 394
码字总数 475753
作品 0
上海
程序员
私信 提问
加载中

评论(2)

韩江华
韩江华
打断点 不行么
韩江华
韩江华
666
使用 React Native 一年后的感受

当我在面试Discord的时候,技术主管Stanislav跟我说: React Native代表着未来。等它一发布,我们就会用它从零构建iOS应用。 作为一名原生iOS开发者,基于先前使用PhoneGap的经验,我非常怀疑...

oschina
2016/06/22
67.9K
31
fir.im Weekly - iOS 开发中的 Git 流程

本期 fir.im Weekly 收集了微博上的热转资源,包含 Android、iOS 开发工具、源码等好用的轮子,还有一些 APP 设计的 Tips,希望对你有用。 精仿知乎日报 iOS 端 @我偏笑_NSNirvana花了将近一...

风起云飞fir_im
2015/11/03
94
0
iOS三维菜单、调试工具、封装通讯录、网络框架、多种控件和动画等源码

iOS精选源码 一个调用系统通讯录和获取通讯录所有联系人的封装(http://www.code4app.com/thread-29726-1-1.html) ios scrollview嵌套tableview同向滑动(初级、进阶), 支持OC / Swift(http...

sunnyaigd
2018/05/15
55
0
那些在学习iOS开发前就应该知道的事(part 2)

英文原文:Things I wish I had known before starting iOS development—Part 2 如果你还没读这篇文章的第一部分,请先读完了再来看第二部分。 那些在学习iOS开发前就应该知道的事(part 1)...

TomatosX
2015/06/12
132
0
iOS 10 正式版发布,重大更新

iOS 10 正式版发布了,这是一个十分重大的 iOS 版本。在 iOS 10 中,很多基础的应用和体验都有了令人耳目一新的感觉。比如全新设计的锁屏体验,增加大量新功能的信息应用,更好玩、功能更全面...

局长
2016/09/14
6.5K
48

没有更多内容

加载失败,请刷新页面

加载更多

Spring Cloud 笔记之Spring cloud config client

观察者模式它的数据的变化是被动的。 观察者模式在java中的实现: package com.hxq.springcloud.springcloudconfigclient;import org.springframework.context.ApplicationListener;i...

xiaoxiao_go
今天
4
0
CentOS7.6中安装使用fcitx框架

内容目录 一、为什么要使用fcitx?二、安装fcitx框架三、安装搜狗输入法 一、为什么要使用fcitx? Gnome3桌面自带的输入法框架为ibus,而在使用ibus时会时不时出现卡顿无法输入的现象。 搜狗和...

技术训练营
今天
4
0
《Designing.Data-Intensive.Applications》笔记 四

第九章 一致性与共识 分布式系统最重要的的抽象之一是共识(consensus):让所有的节点对某件事达成一致。 最终一致性(eventual consistency)只提供较弱的保证,需要探索更高的一致性保证(stro...

丰田破产标志
今天
7
0
docker 使用mysql

1, 进入容器 比如 myslq1 里面进行操作 docker exec -it mysql1 /bin/bash 2. 退出 容器 交互: exit 3. mysql 启动在容器里面,并且 可以本地连接mysql docker run --name mysql1 --env MY...

之渊
今天
7
0
python数据结构

1、字符串及其方法(案例来自Python-100-Days) def main(): str1 = 'hello, world!' # 通过len函数计算字符串的长度 print(len(str1)) # 13 # 获得字符串首字母大写的...

huijue
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部