個人女神——簡潔實用的iOS代碼調試框架

個人女神——簡潔實用的iOS代碼調試框架

1、引言

        這篇博客的起源是接手了公司的一個已經完成的項目,來作代碼優化,項目工程很大,而且引入了不少公司內部的SDK,要搞清楚公司內部的這套框架,的確不是件容易的事,而且因爲這個項目是多人開發的,在調試階段會打印出巨量的調試信息,使得瀏覽有用信息變的十分困難,更加恐怖的是,不少信息是SDK中的調試打印,將這些都進行註銷是很是費勁甚至不可能的事,因而便有了這樣一些需求:首先,我須要清楚瞭解各個controller之間的跳轉關係,須要快速的弄清每一個stroyBoard中各個controller的前因後果,其次,我想在不改變其餘人的調試代碼的狀況下,屏蔽冗餘的log信息,讓個人調試數據更加清晰明瞭。因而我想到了以下的解決方案,一樣,若是你有更好的方案或者你知道的優秀的解決辦法,請告知我,十分感謝。設計模式

2、追蹤程序的跳轉路徑

        這是一個很容易解決的問題,咱們都知道,一個controller,若是要展示出來,必定會走生命週期中的viewWillAppear這個方法,咱們只須要在這個方法中作些手腳就能夠了,實現有兩種思路,一種是採用工廠的設計模式,創建工廠類Controller,在其viewWillAppear中加入咱們的調試代碼,但這對於個人項目並不實用,首先我不肯定全部controller都會繼承於一個父類,其次,在我沒有找到源頭時,這些類已經在公司的framework中了,我根本沒辦法操做源碼。而第二種方案就是runtime,對的,運行時的OC,沒有不可能。關於runtime的詳細說明,在http://my.oschina.net/u/2340880/blog/489072中有介紹。思路是咱們能夠寫一個方法,替換掉系統的viewWillAppear,在其中加入咱們的調試代碼,這個方法就是Method Swizzing,代碼設計以下:app

//新建一個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);
    
}

3、屏蔽冗餘的log信息

一、系統的NSLog是個什麼玩意

        要打敗咱們的敵人,首先應該瞭解咱們的敵人,咱們想要屏蔽NSLog的打印,先須要清楚NSLog究竟是個什麼玩意。框架

        首先,NSLog的定義以下:ide

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__)

二、實現咱們本身的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);
}

這個函數中其實並無作什麼,加了一個鎖的判斷,僅此而已,核心的控制,就交給咱們的鎖吧:spa

//.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;
}

4、看看咱們的傑做吧

        作完上面的工做後,咱們在appdelegate中如以下的簡單配置:.net

- (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

相關文章
相關標籤/搜索