iOS|Android.全球首页投稿(暂停使用,暂停投稿)iOS Developer

【iOS开发】iOS10 Log调试小工具

2017-04-17  本文已影响1290人  谦言忘语

一个解决iOS10在发布环境下无法查看调试log的小工具

出发点

由于iOS10系统,在发布环境下(打成ipa包安装测试或者发布之后从App Store下载安装的包),使用Xcode已经无法查看我们自己打印的log。所以就做了一个小工具,查看log,便于调试。
楼主的需求是,在安装了APP之后查看log,验证程序是否正常运行。除了在调试的时候用到,在线上包出问题之后,也能通过查看log来定位问题。

效果

楼主将该工具分成了3种模式

废话少说,上图:

悬浮窗模式 预览界面 分享界面 效果示例

使用

//需要导入头文件  #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 < 10; ++i)
    {
        NSLogD(@"这里是测试。%@:%d",@"第一个参数",i);
    }
}

不足之处&需改进的地方

这个只是一个小工具,所以只做了几个我认为比较重要的功能。。主要缺陷:

项目解析

介绍下项目用到的几个类和主要的技术点

项目文件结构
        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
//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 <Foundation/Foundation.h>
#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

NSLogD(...)宏定义中,使用logLevel属性来控制log的类型。用户改变了logLevel就改变了打印的类型。

悬浮窗子按钮的点击事件,分为三个。【预览】,【Xcode】和【关闭】。

//点击事件处理
        _floatWindow.clickBolcks = ^(NSInteger i){
            
            switch (i)
            {
                case 0:
                {
                    //显示沙盒本地log
                    [weakSelf displayLocalLog];
                }
                    break;
                case 1:
                {
                    //Xcode显示log
                    weakSelf.logLevel = SQLogToolManagerLevelLog;
                }
                    break;
                case 2:
                {
                    //隐藏log
                    weakSelf.logLevel = SQLogToolManagerLevelNone;
                }
                    break;
                default:
                    break;
            }
            
        };

其中,【Xcode】【关闭】只是设置了logLevel,而【预览】使用了iOS提供的预览和分享的类UIDocumentInteractionController

//显示沙盒本地log
- (void)displayLocalLog
{
    if (![SQLog getLogFilePath])
    {
        return;
    }
    
    //由文件路径初始化UIDocumentInteractionController
    UIDocumentInteractionController *documentInteractionController  = [UIDocumentInteractionController interactionControllerWithURL:[NSURL fileURLWithPath:[SQLog getLogFilePath]]];
    self.documentInteractionController  = documentInteractionController ;
    documentInteractionController.delegate = self;
    
//    //显示分享文档界面
//    [documentInteractionController  presentOptionsMenuFromRect:[UIScreen mainScreen].bounds inView:[UIApplication sharedApplication].keyWindow.rootViewController.view animated:YES];
    
    //直接显示预览界面
    [documentInteractionController  presentPreviewAnimated:YES];
}

#pragma mark - UIDocumentInteractionControllerDelegate

//在哪个控制器显示预览界面
-(UIViewController *)documentInteractionControllerViewControllerForPreview:(UIDocumentInteractionController *)controller
{
    return [UIApplication sharedApplication].keyWindow.rootViewController;
}

参考

谦言万语

蓝天白云,嫩草明湖,小屋人家,大雁南飞……

上一篇 下一篇

猜你喜欢

热点阅读