NSLog 使用

在XCode做开发调试时往往需要打印一些调试信息做debug用

NSLog 性能问题

它的运行会占用时间和设备资源。当打印信息的地方多了之后在模拟器上跑可能不会有什么问题,因为模拟器用的是电脑的硬件,但是当应用跑在设备上时这些输出语句会在很大程度上影响应用的性能,而且输出的数据也可能会暴露出App里的保密数据,所以发布正式版时需要把这些输出全部屏蔽掉,针对这种问题可以写一些宏来控制这些调试信息的输出。

解决方案:

简单粗暴的解决方案:在APP release前,将所有的NSLog注释掉,简单有效,但副作用是:下次你要调试时,又得将NSLog一个个取消注释。

正确的解决方案:你以release模式编译的程序不会用NSLog输出,而你以debug模式编译的程序将执行NSLog的全部功能。 在release版本禁止输出NSLog内容。

如何实现?

  1. 在 xxx.pch (预编译文件) 中添加以下代码:
1
2
3
4
5
6
7
8
//用宏指令做一个判断,如果DEBUG为真,则编译#ifdef到#endif宏定义,否则编译器就不编译;
#ifdef DEBUG
#define NSLog(...) NSLog(__VA_ARGS__)
#define debugMethod() NSLog(@"%s", __func__)
#else
#define NSLog(...)
#define debugMethod()
#endif
  1. 设置DEBUG

在 “Target > Build Settings > Preprocessor Macros > Debug” 里有一个”DEBUG=1”。

设置为Debug模式下,点击Product –> Scheme –> Edit Scheme

设置run –> info –> Build Configuration成Debug时,就可以打印nslog了。

设置Release,发布app版本的时候就不会打印了,提高了性能。

如何自定义输入

在 xxx.pch 中添加以下代码:

1
2
3
4
5
#ifdef DEBUG
#define NSLog(format, ...) fprintf(stderr, "class:%s \nline: %d \nmethod:%s \nmessage:%s \n%s \n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String],__LINE__, __func__,[[NSString stringWithFormat:format, ##__VA_ARGS__] UTF8String], [@"----------------------------------------------" UTF8String]);
#else
#define NSLog(format, ...)
#endif

可以根据个人需要调整上面代码,主要就是这几个宏 ##__VA_ARGS , \FILE , \LINE 和\FUNCTION__。

1
2
3
4
5
1) __VA_ARGS__ 是一个可变参数的宏,很少人知道这个宏,这个可变参数的宏是新的C99规范中新增的,目前似乎只有gcc支持(VC6.0的编译器不支持)。宏前面加上##的作用在于,当可变参数的个数为0时,这里的##起到把前面多余的","去掉的作用,否则会编译出错, 你可以试试。
2) __FILE__ 宏在预编译时会替换成当前的源文件名
3) __LINE__宏在预编译时会替换成当前的行号
4) __FUNCTION__宏在预编译时会替换成当前的函数名称
5) __func__ 打印当前函数或方法,c字符串

将Log日志重定向输出到文件

对于真机,日志没法保存,不好分析问题。所以有必要将日志保存到应用的Docunment目录下,并设置成共享文件,这样才能取出分析。

首先是日志输出,分为c的printf和标准的NSLog输出,printf会向标准输出(sedout)打印,而NSLog则是向标准出错(stderr),我们需要同时让他们都将日志打印到一个文件中。

1
2
3
//例子:
freopen("xx.log","a+",stdout);
freopen("xx.log","a+",stderr);

具体做法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 将NSlog打印信息保存到Document目录下的文件中
- (void)redirectNSlogToDocumentFolder
{    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentDirectory = [paths objectAtIndex:0];
    NSString *logFilePath = [documentDirectory stringByAppendingPathComponent:@"log.txt"];

    // 先删除已经存在的文件
    NSFileManager *defaultManager = [NSFileManager defaultManager];
    [defaultManager removeItemAtPath:logFilePath error:nil];

    // 将log输入到文件
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);
    freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
}

此函数要在- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions中调用。

当连接Mac调试的时候把这些注释掉,否则log只会输入到文件中,而不能从xcode的监视器中看到。

最后配置共享文件夹:

在应用程序的Info.plist文件中添加UIFileSharingEnabled键(Application supports iTunes file sharing 键),并将键值设置为YES。将您希望共享的文件放在应用程序的 Documents目录。一旦设备插入到用户计算机,iTunes 9.1就会在选中设备的Apps标签中找到自己的应用,查看共享内容,找到 log.txt 文件。

断言NSAssert()的使用

NSAssert()只是一个宏,用于开发阶段调试程序中的Bug,通过为NSAssert()传递条件表达式来断定是否属于Bug,满足条件返回真值,程序继续运行,如果返回假值,则抛出异常,并切可以自定义异常描述。NSAssert()是这样定义的:

1
#define NSAssert(condition, desc)

condition是条件表达式,值为YES或NO;desc为异常描述,通常为NSString。当conditon为YES时程序继续运行,为NO时,则抛出带有desc描述的异常信息。NSAssert()可以出现在程序的任何一个位置。具体事例如下:

生成一个LotteryEntry对象时,传入的NSDate不能为nil,加入NSAssert()判断。对象初始化源码如下:

1
2
3
4
5
6
7
8
9
10
- (id)initWithEntryDate:(NSDate *)theDate {    self = [super init];
    if (self) {        NSAssert(theDate != nil, @"Argument must be non-nil");
        entryDate = theDate;
        firstNumber = (int)random() % 100 + 1;
        secondNumber = (int)random() % 100 + 1;
    }
    return  self;
}

接下来则是生成对象时传入一个值为nil的NSDate,看断言是否运行。

LotteryEntry *nilEntry = [[LotteryEntry alloc] initWithEntryDate:nil];

断言效果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
2013-01-17 20:49:12.486 lottery[3951:303] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Argument must be non-nil'

*** First throw call stack:
(
0   CoreFoundation                      0x00007fff90c590a6 __exceptionPreprocess + 198
1   libobjc.A.dylib                     0x00007fff8fd2a3f0 objc_exception_throw + 43
2   CoreFoundation                      0x00007fff90c58ee8 +[NSException raise:format:arguments:] + 104
3   Foundation                          0x00007fff88dae6a2 -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 189
4   lottery                             0x0000000100001929 -[LotteryEntry initWithEntryDate:] + 249
5   lottery                             0x0000000100001794 main + 932
6   libdyld.dylib                       0x00007fff8d83f7e1 start + 0
)
libc++abi.dylib: terminate called throwing an exception

移除NSAssert比较简单,我们需要在target中选择build settings, 找到 preprocessor macros(预处理宏)项目,配置它的release为 NS_BLOCK_ASSERTIONS。

具体操作步骤为: 双击release的空白处,此时会弹出对话框,点击对话框中的+添加NS_BLOCK_ASSERTIONS。

NSLog使用详解与性能分析相关推荐

  1. UIWebView、WKWebView使用详解及性能分析

    一.整体介绍 UIWebView自iOS2就有,WKWebView从iOS8才有,毫无疑问WKWebView将逐步取代笨重的UIWebView.通过简单的测试即可发现UIWebView占用过多内存,且 ...

  2. mysql show profile详解_SQL 性能分析利器 show profile

    本文首发个人公众号<andyqian>, 期待你的关注- 前言 在之前的文章中,我们提到过一些慢SQL优化的步骤.其中就包括:使用 explain 关键字来查看执行计划,是否命中索引. 通 ...

  3. Mysql进阶优化篇01——四万字详解数据库性能分析工具(深入、全面、详细,收藏备用)

    前 言 ?? 作者简介:,长跑型选手,立志坚持写10年博客,专注于java后端 ?? 专栏简介:mysql进阶,主要讲解mysql数据库进阶知识,包括索引.数据库调优.分库分表等 ?? 文章简介:本文 ...

  4. Redis批量操作详解及性能分析

    通过mget批量执行指令可以节约网络连接和数据传输开销,在高并发场景下可以节约大量系统资源.本文中,我们更进一步,比较一下redis提供的几种批量执行指令的性能. 1. 为什么需要批量执行redis指 ...

  5. vmstat命令详解——linux性能分析

    版本信息: procps version 3.2.8 基本使用: vmstat [-a] [-n] [-t] [-S unit] [delay [ count]] [-a]:显示活跃和非活跃内存 [- ...

  6. 并发编程五:java并发线程池底层原理详解和源码分析

    文章目录 java并发线程池底层原理详解和源码分析 线程和线程池性能对比 Executors创建的三种线程池分析 自定义线程池分析 线程池源码分析 继承关系 ThreadPoolExecutor源码分 ...

  7. 【JY】YJK前处理参数详解及常见问题分析(五):风荷载信息

    点击蓝字  求求关注 [风荷载信息详解] 一.基本参数 A区参数详解 1.执行规范 选择所执行的规范. 2.地面粗糙度类别 分为A.B.C.D四类. 3.修正后的基本风压 这里所说的修正后的基本风压, ...

  8. EMD算法之Hilbert-Huang Transform原理详解和案例分析

    目录 Hilbert-Huang Transform 希尔伯特-黄变换 Section I 人物简介 Section II Hilbert-Huang的应用领域 Section III Hilbert ...

  9. java list用法_Java List 用法详解及实例分析

    Java List 用法详解及实例分析 Java中可变数组的原理就是不断的创建新的数组,将原数组加到新的数组中,下文对Java List用法做了详解. List:元素是有序的(怎么存的就怎么取出来,顺 ...

最新文章

  1. 浅谈Chatbot的架构模型和响应机制
  2. alias重启终端失效的问题
  3. python里pickle模块
  4. Gluster的搭建和使用
  5. Redis的两种持久化机制RDB和AOF
  6. java求职攻略_2020年求职攻略 《Java面试连成诀》教程免费分享
  7. JavaScript中map函数和filter的简单举例
  8. (转)Windows 批处理实现 定时打开IE 延时一段时间后 关闭IE
  9. 受众定向_我如何了解社区与受众之间的差异
  10. 10-5-展示后台数据
  11. 使数据可供ArcGIS Server访问
  12. mysql性能剖析工具_MySQL性能剖析工具(pt-query-digest)【转】
  13. MySQL入门02-MySQL二进制版本快速部署
  14. 【Android综合应用】概述
  15. 点石成金 访客至上的网页设计秘笈 (全一点)
  16. 比Spark更适合工业互联网的数据库——热门时序数据库介绍与核心文档汇总【施工中,欢迎留言加入】
  17. 绿联网卡转接mac设置_苹果中文系统USB外置网卡驱动安装方法
  18. 中国物联网激荡20年
  19. REDIS04_主从复制概述及搭建、反客为主、薪火相传、原理、哨兵模式、集群搭建
  20. LabVIEW控制高速微快门

热门文章

  1. QT跨平台使用Web引擎
  2. vue3 学习之路1
  3. Vue Input 失去焦点 @blur事件 获得焦点 ref
  4. 集团大公司企业邮箱安全么?集团企业邮箱注册价格
  5. [读书笔记] 计算机组成原理 唐朔飞 (二)系统总线
  6. 【大数据】Hive可视化工具dbeaver
  7. (常用类_String类)
  8. 机器学习算法工程师面试总结
  9. 【2022年】注册美区ID? 有手就行!
  10. FFMPEG 之 AVFormat