iOS 日志

一、NSLog 介绍:

 开发中输出日志都会用到它。它相当于C语言中得printf,java中得System.out.println()。最常用的就是文字输出、日志输入等。

NSLog定义在NSObjCRuntime.h中,如下所示:

void NSLog(NSString *format,…);
NSLog(@"输出文字");
NSLog(@"第%d名",3);
​​​​​​​NSLog(@“%@,%@,%d",@"1",@"2",3);

输出格式:

%@ 对象

%d, %i 整数

%u,%z 无符整形

%f 浮点/双字

%x, %X 十六进制整数

%o 八进制整数

%zu size_t

%p 指针

%e 浮点/双字 (科学计算)

%g 浮点/双字

%s C字符串

%.*s Pascal字符串

%c 字符

%C unichar

%lld 64位长整数(long long

%llu 无符64位长整数

%Lf 64位双字

%hhdBOOL布尔类型

二、NSLog相比printf有什么好处呢?或者说有什么不同呢?

1、NSLog会自动加上换行符。

2、NSLog在Debug下会写到system.log中。

3、NSLog会自动加上时间和进程信息。

4、NSLog支持%@去打印一个对象类型,当使用%@时,它会给对象发送消息description,如果想要打印类中的关键信息,可通过重载description方法来实现。

5、NSLog完全具备printf的功能,而printf只能打印纯C语言的变量,不能打印OC中的对象。

开发中会经常需要查看view的frame、bounds等信息,因为frame、bounds都是CGRect类型,属于结构体类型。

我们通常打印则需要frame.size.width之类来进行打印,通过拼写NSLog参数进行打印。其实不用这样,苹果已经为我们准备好了相互的转换方法:

这些全部定义在UIGeometry.h中

UIKIT_EXTERN NSString *NSStringFromCGPoint(CGPoint point);

UIKIT_EXTERN NSString *NSStringFromCGVector(CGVector vector);

UIKIT_EXTERN NSString *NSStringFromCGSize(CGSize size);

UIKIT_EXTERN NSString *NSStringFromCGRect(CGRect rect);

UIKIT_EXTERN NSString *NSStringFromCGAffineTransform(CGAffineTransform transform);

UIKIT_EXTERN NSString *NSStringFromUIEdgeInsets(UIEdgeInsets insets);

UIKIT_EXTERN NSString *NSStringFromUIOffset(UIOffset offset);

UIKIT_EXTERN CGPoint CGPointFromString(NSString *string);

UIKIT_EXTERN CGVector CGVectorFromString(NSString *string);

UIKIT_EXTERN CGSize CGSizeFromString(NSString *string);

UIKIT_EXTERN CGRect CGRectFromString(NSString *string);

UIKIT_EXTERN CGAffineTransform CGAffineTransformFromString(NSString *string);

UIKIT_EXTERN UIEdgeInsets UIEdgeInsetsFromString(NSString *string);

UIKIT_EXTERN UIOffset UIOffsetFromString(NSString *string);

例如:打印frame

NSLog(@"%@", NSStringFromCGRect(self.view.frame));

三、NSLog输出重定向

iOS上,Debugstdoutstderr默认都是直接输出到Console, 非Debug时会保存在Systemlog里面。

#define stdout __stdoutp
#define stderr __stderrp
extern FILE *__stdoutp;
extern FILE *__stderrp;
typedef struct __sFILE {
unsigned char *_p; /* current position in (some) buffer */
int _r; /* read space left for getc() */
int _w; /* write space left for putc() */
short _flags; /* flags, below; this FILE is free if 0 */
short _file; /* fileno, if Unix descriptor, else -1 */
struct __sbuf _bf; /* the buffer (at least 1 byte, if !NULL) */
int _lbfsize; /* 0 or -_bf._size, for inline putc */
/* operations */
void *_cookie; /* cookie passed to io functions */
int (* _Nullable _close)(void *);
int (* _Nullable _read) (void *, char *, int);
fpos_t (* _Nullable _seek) (void *, fpos_t, int);
int (* _Nullable _write)(void *, const char *, int);
/* separate buffer for long sequences of ungetc() */
struct __sbuf _ub; /* ungetc buffer */
struct __sFILEX *_extra; /* additions to FILE to not break ABI */
int _ur; /* saved _r when _r is counting ungetc data */
/* tricks to meet minimum requirements even when malloc() fails */
unsigned char _ubuf[3]; /* guarantee an ungetc() buffer */
unsigned char _nbuf[1]; /* guarantee a getc() buffer */
/* separate buffer for fgetln() when line crosses buffer boundary */
struct __sbuf _lb; /* buffer for fgetln() */
/* Unix stdio files get aligned to block boundaries on fseek() */
int _blksize; /* stat.st_blksize (may be != _bf._size) */
fpos_t _offset; /* current lseek offset (see WARNING) */
} FILE;

以上是 stdout 和 stderr 在iOS中的定义,FILE这个结构包含了文件操作的基本属性,对文件的操作都要通过这个结构的指针来进行。其中_file是文件描述符,stdout和stderr的文件描述符已经在系统中定义:

#define STDOUT_FILENO 1 /* standard output file descriptor */
#define STDERR_FILENO 2 /* standard error file descriptor */

使用freopen函数将输出重定向到文件。

freopen详细介绍可见http://www.cplusplus.com/reference/cstdio/freopen/
具体实现如下:

NSString *documentpath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
NSString *fileName = [NSString stringWithFormat:@"%@.log",[NSDate date]];
NSString *logFilePath = [documentpath stringByAppendingPathComponent:fileName];
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stderr);
freopen([logFilePath cStringUsingEncoding:NSASCIIStringEncoding], "a+", stdout);

通过 NSPip 输出到UITextView 

 1、通过NSPipe创建一个管道,pipe有读端和写端。
2、通过dup2将标准输入重定向到pipe的写端。
3、通过NSFileHandle监听pipe的读端,然后将读出的信息显示在UITextview上。

- (void)redirectNotificationHandle:(NSNotification *)nf{
    NSData *data = [[nf userInfo] objectForKey:NSFileHandleNotificationDataItem];
    NSString *str = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
    
    textView.text = [NSString stringWithFormat:@"%@\n%@",textView.text, str];
    NSRange range;
    range.location = [textView.text length] - 1;
    range.length = 0;
    [textView scrollRangeToVisible:range];
    
    [[nf object] readInBackgroundAndNotify];
}
- (void)redirectSTD:(int )fd{
    NSPipe * pipe = [NSPipe pipe] ;
    NSFileHandle *pipeReadHandle = [pipe fileHandleForReading] ;
    dup2([[pipe fileHandleForWriting] fileDescriptor], fd) ;
    
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(redirectNotificationHandle:) name:NSFileHandleReadCompletionNotification object:pipeReadHandle] ;
    [pipeReadHandle readInBackgroundAndNotify];
}
[self redirectSTD:STDOUT_FILENO];
[self redirectSTD:STDERR_FILENO];

四、NSLog使用中存在的问题

NSLog会输出时间、进程等相关信息,它会占用时间和设备资源。当使用模拟器进行开发的时候,NSLog占用资源不明显。大量的log信息在真机上会变卡。卡。卡。卡。卡。卡。卡成狗了有没有。

1、最直接的办法就是在release版本中注释掉NSLog。缺点的是开发过程中还需要重新添加。

2、使用宏进行解决:

#ifndef __OPTIMIZE__

#define NSLog(...) NSLog(__VA_ARGS__)

#else

#define NSLog(...) {}
或者

#ifdef DEBUG

#define NSLog(…) NSLog(__VA_ARGS__)

#else

#define NSLog(…)

#endif

由于__OPTIMIZE__只有在release下才会定义,DEBUG只有在debug下才会定义,所以上述宏即可解决我们的问题。记得将这个宏添加在项目的prefix.pch文件中。

五、去掉NSLog打印的时间戳、进程等信息呢

#ifdef DEBUG

#define NSLog(FORMAT, ...) fprintf(stderr,"%s\n",[[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);

#else

#define NSLog(...)

#endif

意思就是将NSLog的参数通过fprintf打印出来,例如我们要打印NSLog(@“第%d名”,3),转换后就变为fprintf(stderr,”%s\n”,[[NSString stringWithFormat:@”第%d名”, 3] UTF8String])

如果我们要打印类名和行号,则需要在log中加入__FILE__和__LINE__,

例如:

#ifdef DEBUG

#define NSLog(FORMAT, ...) fprintf(stderr,"%s:%d\t%s\n",[[[NSString stringWithUTF8String:__FILE__] lastPathComponent] UTF8String], __LINE__, [[NSString stringWithFormat:FORMAT, ##__VA_ARGS__] UTF8String]);

#else

#define NSLog(...)

#endif

五、常用日志第三方SDK:

1.NSLogger

2.CocoaLumberjack

你可能感兴趣的:(iOS,知识点总结,-,ios,objective-c,日志,NSLog)