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

f5465f52dfb3a37b049786feace62d90.png

作者 | 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中的一个例子,用截图展示:

5c741f8d7292e0ffa578de9d472ff015.png

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/244849.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

电大计算机组成原理ppt,四川电大计算机组成原理(0023)第二次形考作业(课程号:5110023).docx...

四川电大计算机组成原理(0023)第二次形考作业(课程号:5110023).docx 计算机组成原理(0023)第二次形考作业四川电大形成性测评系统 课程代码5110023 参考资料 、单项选择题(共 7 道试题,共 35 分。)1. 组成一个运算器需要多个部件,但下面所列_…

android系统里面的mic是哪个app_安利 | 那些错过会后悔一年的法语APP

如今手机使用的频率越来越高,感觉每一个人都是“低头族”。既然我们都离不开手机,那我们何不利用手机来学习法语呢?今天我就跟大家推荐一些超级好用的各类法语APP,每个APP会注明IOS系统和android系统是否兼容。提高dicte和单词拼写…

北斗通信运营商_国内首个“北斗+5G”应用方案发布 配套5G产品将于年底量产上市...

北京合众思壮科技股份有限公司研发的各类北斗导航应用终端 郭超凯 摄北京合众思壮科技股份有限公司研发的各类北斗导航应用终端 郭超凯 摄中新网郑州9月10日电 (郭超凯)中国卫星导航与位置服务第八届年会10日在郑州召开,北京合众思壮科技股份有限公司(以下简称合众思…

Redis-运维

转自 极客时间 Redis 亚风 原文视频:https://u.geekbang.org/lesson/535?article681062 Redis 同步 Redis主从数据同步,主从第⼀次同步是全量同步 replicaof 主机 端口 #当前这个机器做Master的备份master如何判断slave是不是第⼀次来同步数据: Repl…

python括号的区别_Python中类-带括号与不带括号的区别

类不带括号我们叫赋值,带括号我们叫实例化。 什么是赋值? a7 ba id(7) 140726814208448 id(a) 140726814208448 id(b) 140726814208448 从上面例子中我们可以看出变量a赋值7、b赋值a,它们的内存地址都是相同的。 它们是指向了同一个内存地址&…

初中教师资格证计算机试讲教案模板,教案模板:教师资格证面试初中英语万能教案模板...

【摘要】授课是一门艺术,好的授课方式可以使学生举一反三,培养学生自主学习能力。此时,课前的教案准备工作就极为重要。那么如何写出一篇好的教案呢?考必过为大家精心整理了教案模板:教师资格证面试初中英语万能教案模…

idea yml文件不变成树叶_springboot获取配置文件的三种方式

hello,大家好,有今天没有写文章了,因为呢,最近,公司有点事情,需要我呢,搭建一个demo,框架呢是springbootActive实现消息的延时发送和定时发送功能,为了项目以后中使用到&…

python3.7代码_Centos7源代码安装python3.7,centos7,源码,python37

1、centos7默认安装python2.7,不需要卸载; 2、安装gcc; 3、安装依赖包: yum -y install zlib-devel bzip2-devel openssl-devel ncurses-devel sqlite-devel readline-devel tk-devel gdbm-devel db4-devel libpcap-devel xz-deve…

小米相机摄像头测试软件,摄像头拍照效果测试

◆ 摄像头拍照效果评测摄像头已成为时下任何一款手机都应该有的基本设备了,经过几年的发展,目前主流智能手机所配备的摄像头俨然到达了动辄500W像素以上的级别,而旗舰产品基本都会配备一颗不失体面的800W像素以上的摄像头。小米手机也搭配了一…

电脑关闭计算机怎么重启计算机,教您电脑关机后总是重启怎么办

电脑关机后自动重启的问题,一直困扰一些朋友,通常就是关闭系统后,电脑又自己开机启动了,我们不得不拔掉电源或强制关机,要解决电脑无法关机变重启的问题就要找到原因然后解决,下面,小编讲处理电…

计算机技能节活动作文,技能节感想作文

技能节感想作文在日常学习、工作和生活中,大家都跟作文打过交道吧,作文可分为小学作文、中学作文、大学作文(论文)。那么你知道一篇好的作文该怎么写吗?下面是小编为大家收集的技能节感想作文,欢迎大家借鉴与参考,希望…

神舟笔记本电源管理软件_笔记本电脑是一直插着电源好,还是拔了电源好?

你说怎么好刚才写【图吧小白教程】散热常识 的时候参考了一些资料,为此咱跑了不少网站关注了不少公众号,这年头有意思,干啥都要关注微信,然后就能拿到资料,我寻思我写这么多文章也没打算给咱自己的微信公众号引流呢&am…

matlab中服从高斯分布的矩阵_一些张量的计算步骤matlab代码

Matlab工具箱Tensor Toolbox,安装链接如下CSDN-专业IT技术社区-登录​blog.csdn.net. ,这是我们定义的张量 ,matlab代码:%% %工具包:Tensor Toolbox clc clear all X1 1:12; X1 reshape(X1,[3,4]); X2 13:24; X2 reshape(X2,[3,4]); %如何用矩阵拼接…

徐波 博士 计算机,徐波教授:医工联合促进智能肿瘤学发展——探秘肿瘤精准治疗中的AI技术...

原标题:徐波教授:医工联合促进智能肿瘤学发展——探秘肿瘤精准治疗中的AI技术编者按:近年来,人工智能(AI)技术在医学领域得到广泛应用。在肿瘤精准治疗时代,AI技术的应用进一步促进了肿瘤患者个体化精准治疗。在第五届…

aspx 判断字符串是否为decimal_python 经典面试题:判断字符串括号是否闭合{}[]()...

前言经典面试题:判断一个字符串里面的括号是否闭合,如:{[{()}]()} 就是一个闭合的字符串。{{()}]} 这个里面 ([)] 括号不对称,这种就是不闭合。python判断闭合解决基本思路:先把左括号添加到一个列表里面,遇…

c++整理程序 dev_C编程从入门到实践:C语言开发工具详解(2)

DEV C是一款经典的轻量级C语言开发工具,其安装大小只有几十兆,并且具有图形视图界面,操作比较容易。在DEV C编码界面中可以使用复制和粘贴等命令,这提高了开发效率。2.3.1安装DEV C要安装DEV C ,步骤如下。(1)在百度中搜索DEV C安装包,双击可执行的exe文件进行安装,首先弹出选择…

@value注解_Java系列之注解

Java 注解(Annotation)又称之为 Java 标注、元数据,是 Java 1.5 之后加入的一种特殊语法,通过注解可以标注 Java 中的类、方法、属性、参数、包等,可以通过反射原理对这些元数据进行访问,注解的使用不会影响…

磁共振线圈分类_收藏:磁共振检查序列及临床应用总结

磁共振检查要用到序列,什么是磁共振序列(Sequence)呢? 序列,简单的讲是指具有一定带宽、一定幅度的射频脉冲与梯度脉冲的有机组合。而射频脉冲与梯度脉冲不同的组合方式构成不同的序列,不同的序列获得的图像有各自的特点。磁共振序…

css unchecked,详细介绍CSS中的伪选择器

说到伪选择器,真的让我体会到了CSS的无比强大,强大到自己貌似都不认识CSS了,有点C# 6.0中一些语法糖带给我们的震撼。。。首先我们可以在VS里面提前预览一下。可以看到,上面的伪类有很多很多,多的让我眼都快瞎了。。。…

matlab安装程序无法启动jvm_天呀!JVM居然还有2两种运行模式

概述JVM有两种运行模式Server与Client。两种模式的区别在于,Client模式启动速度较快,Server模式启动较慢;但是启动进入稳定期长期运行之后Server模式的程序运行速度比Client要快很多。这是因为Server模式启动的JVM采用的是重量级的虚拟机&…