日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

frame中src怎么设置成一个变量_自动格式化打印变量HMLog介绍

發布時間:2023/12/15 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 frame中src怎么设置成一个变量_自动格式化打印变量HMLog介绍 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者 | mao2020
來源 | 掘金,點擊閱讀原文查看作者更多文章

前言

在我初學iOS的時候,經常需要NSLog打印用于調試,有時候還需要打印多個變量:

NSLog(@"xxxx frame=%@ tag=%ld isHidden=%d", NSStringFromCGRect(view.frame), view.tag, view.isHidden);

僅考慮把NSLog用來調試輸出,那寫種代碼就太麻煩了,主要存在著這樣幾個問題:

  • 需要格式化,如%@,%ld,%d

  • 需要設置標簽,以便知道輸出值對應的變量,如frame=,tag=,isHidden=

  • 需要類型轉換,如NSStringFromCGRect

  • 有時候還需要寫一些指定字符串以便于在Console輸出中搜索或過濾,如xxxx

后來接觸到各種各樣的Debug Log,主要利用?__LINE__?和?__func__?可以很方便定位到輸出的位置,但是依然還存在前面3個問題。另外LLDB可以很方便獲取變量值,但在變量較多或需要連續打印的情況下也不夠方便快捷。

那個時候我就產生了一個想法,能不能自己寫一個Debug Log,解決上面這些困擾我的問題?這就是我開發HMLog的初衷,源碼僅有一個HMLog.h文件。

基本用法

項目源碼及demo:https://github.com/chenhuimao/HMLog

以下用法均可在HMLogDemo項目中找到。

HMLog

HMLog最終是基于NSLog輸出,根據前面的例子,使用HMLog的代碼和輸出是這樣的:

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 30, 40)];
view.tag = 333;
HMLog(view.frame);
HMLog(view.frame, view.tag, view.isHidden);

// 輸出如下
// 2020-10-17 15:49:33.356890+0800 HMLogDemo[85956:1573131]
// ================ -[ViewController viewDidLoad] [45] ================
// 0: view.frame = NSRect: {{10, 20}, {30, 40}}
// 2020-10-17 15:49:33.357017+0800 HMLogDemo[85956:1573131]
// ================ -[ViewController viewDidLoad] [46] ================
// 0: view.frame = NSRect: {{10, 20}, {30, 40}}
// 1: view.tag = 333
// 2: view.isHidden = NO

Demo中的一個例子,用截圖展示:

HMPrint

HMPrint則基于printf:

UIView *view = [[UIView alloc] initWithFrame:CGRectMake(10, 20, 30, 40)];
view.tag = 333;
HMPrint(view.frame);
HMPrint(view.frame, view.tag, view.isHidden);

// 輸出如下
// ================ -[ViewController viewDidLoad] [45] ================
// 0: view.frame = NSRect: {{10, 20}, {30, 40}}
//
// ================ -[ViewController viewDidLoad] [46] ================
// 0: view.frame = NSRect: {{10, 20}, {30, 40}}
// 1: view.tag = 333
// 2: view.isHidden = NO

HMFormatString

如果只是需要自動格式化的目標字符串,可以使用

HMFormatString:
self.displayLab.text = HMFormatString(self.view.frame, self.view.tag, @selector(viewDidLoad));
printf("%s", self.displayLab.text.UTF8String);

// ================ -[ViewController getFormatString1] [80] ================
// 0: self.view.frame = NSRect: {{0, 0}, {414, 896}}
// 1: self.view.tag = 0
// 2: @selector(viewDidLoad) = SEL: viewDidLoad

可選參數

所有可選參數都應該在#import "HMLog.h"之前定義好

HMLogEnable / HMPrintEnable

分別控制HMLog和HMPrint的是否生效,默認生效,不生效情況下調用沒有任何效果。如只需要在Debug模式下開啟HMPrint:

// Only enable HMPrint in Debug configuration
#ifdef DEBUG
#define HMPrintEnable 1
#else
#define HMPrintEnable 0
#endif
#import "HMLog.h"

HMLogHeaderFormatString(FUNC, LINE)

控制頭部字符串(注意可以重用FUNC和LINE,或者不使用):

#define HMLogHeaderFormatString(FUNC, LINE) \
[NSString stringWithFormat:@"%s ????? %s:\n", FUNC, FUNC]
#import "HMLog.h"
...

HMPrint(self.navigationItem.title);
HMPrint(self.view.bounds.size, self.view.alignmentRectInsets, self.title, self.automaticallyAdjustsScrollViewInsets, self.navigationController, [self class], @selector(viewDidAppear:));

// 輸出如下
// -[ViewController print2] ????? -[ViewController print2]:
// 0: self.navigationItem.title = HMLogDemo
//
// -[ViewController print2] ????? -[ViewController print2]:
// 0: self.view.bounds.size = NSSize: {414, 896}
// 1: self.view.alignmentRectInsets = {0, 0, 0, 0}
// 2: self.title = (null)
// 3: self.automaticallyAdjustsScrollViewInsets = YES
// 4: self.navigationController =
// 5: [self class] = ViewController
// 6: @selector(viewDidAppear:) = SEL: viewDidAppear:

HMLogPrefix(index, valueString)

控制每個變量輸出的前綴。例如只需要展示下標,則按下面的方式定義:

// Only show index prefix
#define HMLogPrefix(index, valueString) [NSString stringWithFormat:@"%d: ", index]
#import "HMLog.h"
...

HMPrint(self.navigationItem.title);
HMPrint(self.view.bounds.size, self.view.alignmentRectInsets, self.title, self.automaticallyAdjustsScrollViewInsets, self.navigationController, [self class], @selector(viewDidAppear:));

// ================ -[ViewController print2] [79] ================
// 0: HMLogDemo
//
// ================ -[ViewController print2] [80] ================
// 0: NSSize: {414, 896}
// 1: {0, 0, 0, 0}
// 2: (null)
// 3: YES
// 4:
// 5: ViewController
// 6: SEL: viewDidAppear:

HMLogTypeExtension

默認情況下不支持CGVector和CLLocationCoordinate2D類型,可以額外匹配需要格式化的類型:

#define HMLogTypeExtension \
else if (strcmp(type, @encode(CGVector)) == 0) { \
CGVector actual = (CGVector)va_arg(v, CGVector); \
obj = NSStringFromCGVector(actual); \
} else if (strcmp(type, @encode(CLLocationCoordinate2D)) == 0) { \
CLLocationCoordinate2D actual = (CLLocationCoordinate2D)va_arg(v, CLLocationCoordinate2D); \
obj = [NSString stringWithFormat:@"latitude: %lf, longitude: %lf", actual.latitude, actual.longitude]; \
}

#import
#import "HMLog.h"
...

CGVector vector = CGVectorMake(110, 119);
CLLocationCoordinate2D coordinate = CLLocationCoordinate2DMake(22.512145, 113.9155);
HMPrint(vector, coordinate);

// ================ -[CustomizeFormatViewController log] [65] ================
// 0: vector = {110, 119}
// 1: coordinate = latitude: 22.512145, longitude: 113.915500

使用注意

  • 項目需要Foundation和UIKit框架。C語言標準為gnu99,Xcode項目的C Language Dialect選項設置為gnu99或gnu11

  • HMLog項目的實現僅有一個文件HMLog.h,可以在pch文件導入,也可以在每個需要的文件分別導入。所有可選參數都應該在#import "HMLog.h"之前定義好

  • 一次調用最多支持20個變量

  • 沒有支持所有的數據類型,默認支持的類型參考源碼,可以使用HMLogTypeExtension進行擴展

設計思路

只考慮1個變量

首先考慮1個變量的情況,即HMLog只能傳入1個變量。

  • 要格式化1個變量,就要先知道這個變量的類型。變量可以通過__typeof__獲取類型,比如我們常常這樣用__weak __typeof__(self) weakSelf = self;。

  • 取得變量類型后,還需要比較判斷,之后才能把id類型格式化為"%@",把long類型格式化為"%ld"。類型如何做判斷呢?if(long == id)顯然是不行的,OC類型編碼@encode會返回一個char *字符串,這樣就可以利用strcmp函數做比較了:

NSString *format;
if (strcmp(@encode(__typeof__(self.view)), @encode(id)) == 0) {
format = @"%@";
} else if (strcmp(@encode(__typeof__(self.view)), @encode(long)) == 0) {
format = @"%ld";
} else if ...

參考蘋果的文檔,也可以寫成if (strcmp(@encode(__typeof__(self.view)), "@") == 0)的形式。不過HMLog并沒有采用這種簡化的形式,@encode是編譯器指令,并不影響運行時效率,上面的代碼塊中的形式更加直觀。

  • 要把這個功能寫成一個通用函數,那如何表示任意的類型?換句話說,如果value是NSObject對象可以用id value表示,但如果value可能是任何類型,id該換成什么?這個時候,可變參數函數派上用場了,可變參數最后的...,是不需要寫明數據類型的,這樣可以把變量(value)和變量的類型編碼@encode(__typeof__(value))同時傳入進去,同時利用宏把一個變量替換為這兩種形式:

#define MyLog(value) _MyLog(__func__, __LINE__, @encode(__typeof__(value)), (value))

static void _MyLog(const char *func, int line, ...) {
NSMutableString *result = [[NSMutableString alloc] init];
[result appendFormat:@"\n===== %s [%d] =====\n", func, line];

va_list v;
va_start(v, line);
char *type = va_arg(v, char *);
if (strcmp(type, @encode(id)) == 0) {
id actual = (id)va_arg(v, id);
[result appendFormat:@"id: %@\n", actual];
} else if (strcmp(type, @encode(long)) == 0) {
long actual = (long)va_arg(v, long);
[result appendFormat:@"long: %ld\n", actual];
}
va_end(v);
NSLog(@"%@", result);
}

// 可以愉快地打印id和long類型了
MyLog(self.view);
MyLog(self.view.tag);

把上面的例子的條件語句補充好需要的類型,MyLog宏就可以打印任意類型的1個變量了。

考慮多個變量

接下來要考慮的是如何同時打印多個變量。

  • 按照前面的思路,打印多個變量,需要把每個變量(value)和變量的類型編碼@encode(__typeof__(value))都傳給可變參數函數_MyLog,另外還需要一個數量count表示一共有幾組變量及其類型編碼。為了實現這個需求,這里使用了獲取宏參數個數以及遞歸宏的技巧,請閱讀完這篇文章,了解C語言宏定義使用總結與遞歸宏。

  • 最后補充好細節,使變量名稱化為字符串作為提示標簽,定制化使用的可選參數,這就完成了HMLog。

整體思路很清晰,源碼也只有一個200多行的HMLog.h文件,難點基本上只有遞歸宏的使用。除此之外值得一提的還有兩點:

  • float類型的值,通過va_arg獲取值先傳入double類型,然后再強制類型轉換為float類型:float actual = (float)va_arg(v, double);,這是因為有個規則叫默認參數提升,還有一些char、short等類型也是如此。

  • [result appendFormat:@"%@%@\n", ((void)(valueString), HMLogPrefix(i, valueString)), obj];這行代碼,為了消除宏HMLogPrefix(i, valueString)可能沒有用到valueString導致的警告,使用了逗號運算符,這是宏定義使用中常用的一個運算符。

參考資料

[1]https://juejin.im/post/6884575803523203080
[2]https://github.com/SnapKit/Masonry/blob/master/Masonry/MASUtilities.h

總結

以上是生活随笔為你收集整理的frame中src怎么设置成一个变量_自动格式化打印变量HMLog介绍的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。