每个应用都是顺序执行的,在时间序列上都是串行的,每个具体应用在某个时刻只能有一个cpu正常处理,就是多线程也遵循cpu时间串行序列,只有时间一个线程调用中断函数(如:sleep)或一个处理事件完成才调度cpu,若调用了中断函数,线程被挂起,释放cpu控制权,仍保留部分自己线程的资源,以便与该线程再次获取到cpu继续后续处理。这就是我理解的多线程原理和普通应用在时间序列上的串行性。
同步处理就是该处理阻塞应用的其它操作,直到处理该应用处理完成;异步处理是起了另外的线程去处理自己的事情,不阻塞应用的其它处理。
同步处理的优缺点:实现简单便于管理,耗电量较少,可以立即看到处理结果,由于阻塞应用,所以处理要简单,处理不能耗费太长时间,否则严重影响系统性能;异步的优点:实现较复杂,因为起线程耗所以电量较大,得到结果不及时,由于不阻塞线程和结果不需要很及时,所以异步处理可以处理很复杂,可以加入相对较费时间的处理(发送邮件,上传文件到服务器等)。
CocoaLumberjack日志系统就是异步处理,所以它可以继续扩充功能,如:把日志以邮件的形式发送,把日志上传服务器。我设计的同步日志系统就是同步处理,同步日志系统最好别加入超耗时的处理(发送邮件,上传文件到服务器等),以免影响应用的性能。由于是同步日志,是影响性能的,快速打印它吃不消,只能进行间隔时间比较久的个别打印。如:拦截到app崩溃可以使用同步日志实时记录下来,若你用CocoaLumberjack等异步日志打印,由于它有延迟,app都挂了不可能记录下崩溃日志的。
异步日志系统CocoaLumberjack的使用见文章:http://blog.youkuaiyun.com/jia12216/article/details/44412697 。
简单的同步日志(就是对系统日志函数的NSLog进一步封装)可以只实现控制台日志打印,对系统性能基本没有影响(除非出现在循环里打印大量日志会影响系统性能),由于打印信息较少,它对开发人员同步定位问题有用,对普通的测试人员和其它使用人员帮助不大,稍微强过NSLog。
Macro.h实现代码:
//自定义日志开关
#ifdef DEBUG
static const int g_printLogLevel = 3; // 仅当 isPrintLog 值大于0的时候才输出日志,关闭日志打印改为0即可
#else
static const int g_printLogLevel = 0; // 仅当 isPrintLog 值大于0的时候才输出日志,关闭日志打印改为0即可
#endif
#ifndef LogInfo
#define LogInfo(format, ...) \
{ \
if(2 < g_printLogLevel) \
{ \
NSLog((@"%@.m:%d Info:" format), NSStringFromClass([self class]), __LINE__, ## __VA_ARGS__); \
} \
}
#endif
#ifndef LogDebug
#define LogDebug(format, ...) \
{ \
if(1 < g_printLogLevel) \
{ \
NSLog((@"%@.m:%d Debug:" format), NSStringFromClass([self class]), __LINE__, ## __VA_ARGS__); \
} \
}
#endif
#ifndef LogError
#define LogError(format, ...) \
{ \
if(0 < g_printLogLevel) \
{ \
NSLog((@"%@.m:%d Error:" format), NSStringFromClass([self class]), __LINE__, ## __VA_ARGS__); \
} \
}
#endif
PrefixHeader.pch文件
#ifndef MapDemoLocation_PrefixHeader_pch
#define MapDemoLocation_PrefixHeader_pch
#ifdef __OBJC__
#import "Macro.h"
#endif
#endif
使用例子:
LogError(@“LOGERR :%@”, @“TEST”);
自定义4级别同步日志系统实现控制台日志打印,写日志文件,可以指定写的日志文件的最大行数和最多的日志文件个数,超过制定的日志文件个数就删除最早的日志文件并且重新建立一个新的文件,可以自定义release版本是否写日志文件以及何种日志级别需要写日志文件。若功能有百度地图等用到.mm文件打印日志需要修改把Compile Sources As的选项选为Objective-C++(参考文章:http://blog.youkuaiyun.com/wangyuchun_799/article/details/7729222)。
WriteLog.h代码:
//
// WriteLog.h
// MapDemoLocation
//
// Created by 郏国上 on 15/6/8.
// Copyright (c) 2015年 gaos. All rights reserved.
//
#ifndef WriteLog_h
#define WriteLog_h
#define ERR_LOG 1 /* 应用程序无法正常完成操作,比如网络断开,内存分配失败等 */
#define WARN_LOG 2 /* 进入一个异常分支,但并不会引起程序错误 */
#define INFO_LOG 3 /* 日常运行提示信息,比如登录、退出日志 */
#define DEBUG_LOG 4 /* 调试信息,打印比较频繁,打印内容较多的日志 */
#ifndef LOGERR
#define LOGERR(format,...) WriteLog(ERR_LOG,__FUNCTION__,__LINE__,format,##__VA_ARGS__)
#endif
#ifndef LOGWARN
#define LOGWARN(format,...) WriteLog(WARN_LOG,__FUNCTION__,__LINE__,format,##__VA_ARGS__)
#endif
#ifndef LOGINFO
#define LOGINFO(format,...) WriteLog(INFO_LOG,__FUNCTION__,__LINE__,format,##__VA_ARGS__)
#endif
#ifndef LOGDEBUG
#define LOGDEBUG(format,...) WriteLog(DEBUG_LOG,__FUNCTION__,__LINE__,format,##__VA_ARGS__)
#endif
//#ifndef WRITELOGS
//#define WRITELOGS(format,...) WriteFileLog(NSString *string)
//#endif
void WriteLog(int ulErrorLevel, const char *func, int lineNumber, NSString *format, ...);
#endif
WriteLog.m代码:
#import <Foundation/Foundation.h>
#import “WriteLog.h”
NSString getTime(void)
{
NSDateFormatter * formatter = [[NSDateFormatter alloc]init];
[formatter setDateFormat:@“YYYY-MM-dd HH:mm:ss:SSS”];
NSString dateTime = [formatter stringFromDate:[NSDate date]];
return dateTime;
}
void WriteFileLog(NSString format, …)
{
va_list args;
va_start(args, format);
NSString string = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
if(!string)
{
return;
}
NSFileManager fm = [NSFileManager defaultManager];
NSString str = nil;
BOOL flag = NO;
NSArray myPaths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString myDocPath = [myPaths objectAtIndex:0];
NSString* path = [myDocPath stringByAppendingPathComponent:@“Log”];
[fm fileExistsAtPath:path isDirectory:&flag];
NSArray dirarray = nil;
NSString filePath = nil;
NSArray *lines = nil;
NSError error = nil;
NSData data = nil;
NSInteger n = 0, i = 0, m = 0;
NSMutableArray *filesMutableArr = [NSMutableArray array];
NSDictionary *infoDictionary = [[NSBundle mainBundle] infoDictionary];
NSString *app_Name = [infoDictionary objectForKey:@"CFBundleDisplayName"];
if(flag)
{
// dirarray = [fm contentsOfDirectoryAtPath:filePath];
// FLDDLogDebug(@"%@ ",dirarray);
dirarray = [fm contentsOfDirectoryAtPath:path error:nil];
NSLog(@"%@ ",dirarray);
n = dirarray.count;
for(i = 0; i < n; i++)
{
filePath = [path stringByAppendingPathComponent:dirarray[i]];
if ([fm fileExistsAtPath:filePath])
{
[filesMutableArr addObject:filePath];
}
}
m = filesMutableArr.count;
if(m > g_logFilesCount)
{
for(i = m - g_logFilesCount; i > 0; i++)
{
filePath = filesMutableArr[m - 1];
[fm removeItemAtPath:filePath error:nil];
}
}
else if(g_logFilesCount == m)
{
filePath = filesMutableArr[m - 1];
lines = [[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]
componentsSeparatedByString:@"\n"];
if(lines.count < g_logFileLines)
{
data = [NSData dataWithContentsOfFile:filePath];
str =[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
str = [NSString stringWithFormat:@"%@\n%@",str,string];
[str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
else
{
[fm removeItemAtPath:filePath error:nil];
str = [NSString stringWithFormat:@"%@ %@.text", app_Name, getTime()];
filePath = [path stringByAppendingPathComponent:str];
str = string;
[str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
}
else if(m > 0)
{
filePath = filesMutableArr[m - 1];
// str =[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:&error];
// str = [NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil];
// NSLog(@"str :%@", str);
lines = [[NSString stringWithContentsOfFile:filePath encoding:NSUTF8StringEncoding error:nil]
componentsSeparatedByString:@"\n"];
// if(lines.count < 65535)
if(lines.count < g_logFileLines)
{
data = [NSData dataWithContentsOfFile:filePath];
str =[[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
str = [NSString stringWithFormat:@"%@\n%@",str,string];
[str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
else
{
str = [NSString stringWithFormat:@"%@ %@.text", app_Name, getTime()];
filePath = [path stringByAppendingPathComponent:str];
str = string;
[str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
}
else
{
str = [NSString stringWithFormat:@"%@ %@.text", app_Name, getTime()];
filePath = [path stringByAppendingPathComponent:str];
str = string;
[str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}
}
else
{
BOOL res = [fm createDirectoryAtPath:path withIntermediateDirectories:YES attributes:nil error:nil];
if (res) {
// NSLog(@"文件夹创建成功");
str = [NSString stringWithFormat:@"%@ %@.text", app_Name, getTime()];
filePath = [path stringByAppendingPathComponent:str];
str = string;
// NSLog(@"filePath :%@", filePath);
[str writeToFile:filePath atomically:YES encoding:NSUTF8StringEncoding error:&error];
// NSLog(@"error :%@", error);
}
else
{
NSLog(@"文件夹创建失败");
}
}
}
void WriteLog(int ulErrorLevel, const char *func, int lineNumber, NSString *format, …)
{
va_list args;
va_start(args, format);
NSString *string = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
NSString *strFormat = [NSString stringWithFormat:@"%@%s, %@%i, %@%@",@"Function: ",func,@"Line: ",lineNumber, @"Format: ",string];
NSString * strModelName = @"文件日志"; //模块名
NSString *strErrorLevel = [[NSString alloc] init];
NSString *str = nil;
switch (ulErrorLevel) {
case ERR_LOG:
{
strErrorLevel = @"Error";
str = [NSString stringWithFormat:@"ModalName: %@, ErrorLevel: %@, %@.",strModelName, strErrorLevel, strFormat];
//写该级别日志,注释了就不写该级别日志了日志文件了
// WriteFileLog(str);
break;
}
case WARN_LOG:
{
strErrorLevel = @“Warning”;
str = [NSString stringWithFormat:@“ModalName: %@, ErrorLevel: %@, %@.”,strModelName, strErrorLevel, strFormat];
//写该级别日志,注释了就不写该级别日志了日志文件了
// WriteFileLog(str);
break;
}
case INFO_LOG:
{
strErrorLevel = @“INFO”;
str = [NSString stringWithFormat:@“ModalName: %@, ErrorLevel: %@, %@.”,strModelName, strErrorLevel, strFormat];
//写该级别日志,注释了就不写该级别日志了日志文件了
// WriteFileLog(str);
break;
}
case DEBUG_LOG:
{
strErrorLevel = @“Debug”;
str = [NSString stringWithFormat:@“ModalName: %@, ErrorLevel: %@, %@.”,strModelName, strErrorLevel, strFormat];
//写该级别日志,注释了就不写该级别日志了日志文件了
// WriteFileLog(str);
break;
}
default:
break;
}
str = [NSString stringWithFormat:@"ModalName: %@, ErrorLevel: %@, %@.",strModelName, strErrorLevel, strFormat];
NSLog(@"%@", str);
//打印全部级别日志,注释了就不写日志文件了
WriteFileLog(str);
}
PrefixHeader.pch文件
#ifndef MapDemoLocation_PrefixHeader_pch
#define MapDemoLocation_PrefixHeader_pch
// Include any system framework and library headers here that should be included in all compilation units.
// You will also need to set the Prefix Header build setting of one or more of your targets to reference this file.
#ifdef __OBJC__
#ifdef DEBUG
#import "WriteLog.h"
#else //若想release版本也想打印日志就把下面12行注释了。
#ifndef LOGERR
#define LOGERR(format,...) {};
#endif
#ifndef LOGWARN
#define LOGWARN(format,...) {};
#endif
#ifndef LOGINFO
#define LOGINFO(format,...) {};
#endif
#ifndef LOGDEBUG
#define LOGDEBUG(format,...) {};
#endif
#endif
#endif
static const long g_logFilesCount = 10;
static const long g_logFileLines = 65535;
#endif
自定义同步日志函数和NSLOG完全相同,只是函数名不同。默认只debug版本写日志(工程的Scheme要是debug模式),release版本是否写日志。使用例子:
LOGERR(@“LOGERR :%@”, @“TEST”);
LOGWARN(@“LOGWARN :%@”, @“TEST”);
LOGINFO(@“LOGINFO :%@”, @“TEST”);
LOGDEBUG(@“LOGDEBUG :%@”, @“TEST”);
为了倒出日志文件需要在Info.plist文件里增加Application supports iTunes file sharing属性,类型是Boolean,值设置为YES(若设置为NO就不可以通过iTunes导出文件了,可以等发布正式版本时设置为NO).
demo的下载地址是:http://download.youkuaiyun.com/detail/jia12216/8787079
自定义同步日志文件目录:Log
日志文件的导出:
异步日志CocoaLumberjack文件目录:Log
百度地图位置信息csv格式记录目录:LngLat