這篇博客的起源是接手了公司的一個已經完成的項目,來作代碼優化,項目工程很大,而且引入了不少公司內部的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,代碼設計以下: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); }
要打敗咱們的敵人,首先應該瞭解咱們的敵人,咱們想要屏蔽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,這就是宏的強大之處,狸貓換太子,不錯吧。測試
下面咱們來實現咱們的這個函數,以下:優化
//不要忘了在.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; }
作完上面的工做後,咱們在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