原文
一个解决iOS10在发布环境下无法查看调试log的小工具
由于iOS10系统,在发布环境下(打成ipa包安装测试或者发布之后从App Store下载安装的包),使用Xcode已经无法查看我们自己打印的log。所以就做了一个小工具,查看log,便于调试。
楼主的需求是,在安装了APP之后查看log,验证程序是否正常运行。除了在调试的时候用到,在线上包出问题之后,也能通过查看log来定位问题。
楼主将该工具分成了3种模式
这个是默认的模式。默认初始化之后是不显示任何log的。也就是说不做任何操作。
这个模式只在Xcode控制台显示log。这就是我们iOS10出来之前用的模式。通过控制台查看log。但是由于iOS10的时候,真机查看log时候会冒出一大坨没用的log,然后我们自己的log又看不见(好像Xocde8.2.1可以看见,但是还是会有很多无用的log),所以在iOS10下这个模式不好用
这个模式将log显示在Xcode控制台,同时将log写入到沙盒中。我们可以通过读取沙盒的log文件来查看log。并且log文件还可以通过邮件、QQ、微信等方式发送。这个模式就是我们这个小工具主要实现的功能了。值的一说的是,这个模式我们会有一个悬浮窗,用来随时查看沙盒的log文件内容。
废话少说,上图:
悬浮窗模式
预览界面
分享界面
-
第一幅图就是悬浮窗模式。图中为浮窗展开时的界面。3个按钮分别对应着上面说的悬浮窗模式、Xcode控制台显示log模式、不显示log模式。
-
第二幅图是点击在浮窗上的【预览】之后显示的界面,这些就是我们在程序中打印的log。
-
第三幅图是点击第二幅图右上角的分享之后的界面。如果手机安装了QQ微信Facebook等软件,可以通过这些软件来分享这个log文件。
效果示例
-
默认情况下,我们的小工具什么都不做。
-
当我们手动触发某个开关之后,悬浮窗就显示出来。并自动在沙盒创建log文件,之后就可以使用log写入功能。
-
悬浮窗是可以一直漂浮在APP的上方的,便于我们随时点击浮窗,查看log
-
点击悬浮窗上的【预览】按钮就可以以界面的形式显示沙盒文件的log,并且可以选择已邮件QQ微信等形式发送log文件
-
点击悬浮窗上的【Xcode】按钮,悬浮窗会消失,并且只会在Xcode控制台显示log。不会写入到文件。
-
点击悬浮窗上的【关闭】按钮,悬浮窗会消失,并且log不会再打印。
-
重启APP之后,会根据之前的模式来决定是否显示浮窗和打印log。
代码示例:
//需要导入头文件 #import "SQLogToolManager.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
[[SQLogToolManager shareManager] logIntial];
return YES;
}
代码示例:
- (IBAction)onClickWriteToTextAndShowFloatWindow:(UIButton *)sender
{
[SQLogToolManager shareManager].logLevel = SQLogToolManagerLevelText;
}
代码示例:
- (IBAction)onClickTest:(UIButton *)sender
{
for (int i = 0; i
{
NSLogD(@"这里是测试。%@:%d",@"第一个参数",i);
}
}
这个只是一个小工具,所以只做了几个我认为比较重要的功能。。主要缺陷:
介绍下项目用到的几个类和主要的技术点
项目文件结构
继承UIWindow,将windowLevel设置为UIWindowLevelAlert + 1 来使window浮窗悬浮在APP上方。
UIWindow *preKeyWindow = [UIApplication sharedApplication].keyWindow;
self.backgroundColor = [UIColor clearColor];
self.windowLevel = UIWindowLevelAlert + 1;
self.rootViewController = [UIViewController new];
[self makeKeyAndVisible];
//还回keyWindow
if (preKeyWindow) {
[preKeyWindow makeKeyWindow];
}
值得注意的是,我们的悬浮窗不需要作为keyWindow(不然会带来很多麻烦的),所以需要将keyWindow还给之前的window。
给悬浮窗加了一个主按钮和若干个子按钮,用来点击
//添加按钮
[self setupButtons];
//主按钮
[self setupMainBtnWithName:mainBtnName];
定义一个block属性来处理子按钮的点击事件,具体的子按钮由按钮的tag来区分
/**
点击事件block
*/
@property (nonatomic,copy) void(^clickBlocks)(NSInteger i);
浮窗还做了一些点击展开/收回,拖拽,靠边隐藏 等操作,具体请查看源代码
当调用
SQLogD(...)
时,会将log显示在Xcode控制台,并且将log写入到沙盒文件 中。
//目前只用到了SQLogD
//Debug
#define SQLogD(...) [SQLog logD:[NSString stringWithFormat:__VA_ARGS__],@""];
调用
logIntial
初始化方法时,会在沙盒创建log.txt文件(如果已创建就清空文件内容)。
当调用
SQLogD(...)
打印log时,会创建一个异步的串行队列来进行打印并写入log。
+ (void)logvLevel:(SQLogLevel)level Format:(NSString *)format VaList:(va_list)args
{
__block NSString *formatTmp = format;
dispatch_async(k_operationQueue, ^{//异步串行队列
if (level >= LogLevel)//只有大于当前LogLevel级别的log才会进行操作
{
formatTmp = [[SQLog SQLogFormatPrefix:level] stringByAppendingString:formatTmp];
//写入log文件的同时,在控制台打印出来
NSLog(@"%@",formatTmp);
NSString *contentStr = [[NSString alloc] initWithFormat:formatTmp arguments:args];
NSString *contentN = [contentStr stringByAppendingString:@"\n"];
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss"];
//设置为北京时间
NSTimeZone *timeZone = [NSTimeZone timeZoneForSecondsFromGMT:8];//直接指定时区,这里是东8区
NSInteger seconds = [timeZone secondsFromGMTForDate: [NSDate date]];
NSDate *beiJingDate = [NSDate dateWithTimeInterval: seconds sinceDate: [NSDate date]];
//这里是最终打印出来的字符串,可以根据需要加一些参数进去
NSString *content = [NSString stringWithFormat:@"%@ %@", [dateFormatter stringFromDate:beiJingDate], contentN];
//使用NSFileHandle来写入数据
NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:logFilePath];
[file seekToEndOfFile];
[file writeData:[content dataUsingEncoding:NSUTF8StringEncoding]];
[file closeFile];
formatTmp = nil;
}
});
}
#import
#import "SQLog.h"
/**
log显示宏定义。
@param ... 可变参数,跟NSLog一致
*/
#define NSLogD(...) do{\
switch ([SQLogToolManager shareManager].logLevel) {\
case SQLogToolManagerLevelNone:{} break;\
case SQLogToolManagerLevelLog:{\
NSLog(__VA_ARGS__);} break;\
case SQLogToolManagerLevelText:{\
SQLogD(__VA_ARGS__);} break;\
default: break;}\
} while (0);
typedef enum
{
SQLogToolManagerLevelNone = 0, //不打印log
SQLogToolManagerLevelLog = 1, //只在控制台显示log
SQLogToolManagerLevelText = 2 //在控制台显示log及在本地写入log
} SQLogToolManagerLevel;
@interface SQLogToolManager : NSObject
/**
logLevel
*/
@property (nonatomic, assign) SQLogToolManagerLevel logLevel;
/**
单例
*/
+ (instancetype)shareManager;
/**
初始化
*/
- (void)logIntial;
@end
悬浮窗子按钮的点击事件,分为三个。【预览】,【Xcode】和【关闭】。
//点击事件处理