mac-手把手做一个解析崩溃.ips的Mac-应用

701 阅读2分钟

一. 为何要做这个工具(why make this tool)

因为手动操作很繁杂,如果做成app,一键解析崩溃文件,那就会相当nice

image.png app: 工具在这里下载:链接: pan.baidu.com/s/1gMbrW8ry… 提取码: h4bg

解析崩溃前提知识储备:

使用symbolicatecrash解析了一个crash log

本地ips文件解析工具

iOS手动解析崩溃堆栈

Mac开发需要的知识储备:

检测拖拽的文件路径

NSFileHandle文件操作神器

NSTask介绍,指令、调用外部程序

你也可以直接忽略,直接开干

首先创建一个mac-应用

image.png

监控推入的文件和获取文件

此类功能就是监控推入的文件路径

#import <Cocoa/Cocoa.h>
@protocol DragDropViewDelegate;
@interface DragDropView : NSView
@property (assign) id<DragDropViewDelegate> delegate;
@end

@protocol DragDropViewDelegate <NSObject>
-(void)dragDropViewFileList:(NSArray)fileList;
@end

#import "DragDropView.h"

@implementation** DragDropView
@synthesize delegate = _delegate;

- (id)initWithFrame:(NSRect)frame{
    self = [super initWithFrame:frame];
    if (self) {
    //这里我们只添加对文件进行监听,如果拖动其他数据类型到view中是不会被接受的
    [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
    }
    return self;
}

- (instancetype)init{
    self = [super init];
    if (self) {
    //这里我们只添加对文件进行监听,如果拖动其他数据类型到view中是不会被接受的
    [self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, **nil**]];
    }
    return self;
}

-(NSDragOperation)draggingEntered:(id<NSDraggingInfo>)sender{
    NSPasteboard *pboard = [sender draggingPasteboard];
    if ([[pboard types] containsObject:NSFilenamesPboardType]) {
        return NSDragOperationCopy;
    }
    return NSDragOperationNone;
}

-(BOOL)prepareForDragOperation:(id<NSDraggingInfo>)sender{
    // 1)、获取拖动数据中的粘贴板
    NSPasteboard *zPasteboard = [sender draggingPasteboard];
    // 2)、从粘贴板中提取我们想要的NSFilenamesPboardType数据,这里获取到的是一个文件链接的数组,里面保存的是所有拖动进来的文件地址,如果你只想处理一个文件,那么只需要从数组中提取一个路径就可以了。
    NSArray *list = [zPasteboard propertyListForType:NSFilenamesPboardType];
    // 3)、将接受到的文件链接数组通过代理传送
    if(self.delegate && [self.delegate respondsToSelector: @selector**(dragDropViewFileList:)])
    [self.delegate dragDropViewFileList:list];
    return YES;
}

- (void)drawRect:(NSRect)dirtyRect{
     Drawing code here.
}

- (**void**)dealloc {
    [self setDelegate:nil];
}
@end

运用NSTask打开symbolicatecrash应用,并执行解析崩溃

- (void)windowDidLoad{
    [super windowDidLoad];
    [self setUpView];
}

- (void)setUpView{
    [self.dragDropView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType, nil]];
    [self.dragDropView setDelegate:self];
}

-(void)dragDropViewFileList:(NSArray *)fileList{
//    NSLog(@"%@",fileList);
    self.crashFilePath = nil;
    self.dsymlFilePath = nil;
    [self.logOutView setString:NSLocalizedString(@"defaultTitle", nil)];
    for (NSString* filePath in fileList) {
        if ([filePath rangeOfString:@".ips"].length > 0 ){
            self.crashFilePath = filePath;
        }
        if([filePath rangeOfString:@"dSYM"].length > 0){
            self.dsymlFilePath = filePath;
        }
    }
    if (self.crashFilePath == nil && self.crashFilePath.length <= 0){
        [self AlertViewShowWithMessageText:NSLocalizedString(@"warning", @"") InforMativeText:NSLocalizedString(@"nofile", @"")];
        return;
    }
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
     [self beginToAnalysisCrashFile];
    });
    //获取文件目录,开始解析
}

-(void)beginToAnalysisCrashFile{
    NSPipe *pipe = [NSPipe pipe];
    NSFileHandle *file = pipe.fileHandleForReading;
    NSTask *analysisTask = [[NSTask alloc] init];
    NSString *launchPath = @"/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/symbolicatecrash";
    if (launchPath==nil||!([launchPath rangeOfString:@"Xcode.app"].length>0)){
        [self AlertViewShowWithMessageText:NSLocalizedString(@"warning", @"")  InforMativeText:NSLocalizedString(@"noXcode", @"") ];
    }else{
       analysisTask.launchPath = launchPath;
    }
    NSArray *array = [NSArray arrayWithObjects:self.crashFilePath, self.dsymlFilePath, nil];
    [analysisTask setArguments:array];
    [analysisTask setEnvironment:@{
                                  @"DEVELOPER_DIR":@"/Applications/Xcode.app/Contents/Developer"
                                  }];
    [analysisTask setStandardOutput:pipe];
    [analysisTask launch];
    NSData *data = [file readDataToEndOfFile];
    [file closeFile];
    NSString *LogOutput = [[NSString alloc] initWithData: data encoding: NSUTF8StringEncoding];
    }

//    _LogOut=[_LogOut stringByAppendingString:[NSString stringWithFormat:@"\n%@",LogOutput]];
    [self writeToFileWithContent:LogOutput];
}

-(void)writeToFileWithContent:(NSString*)logOut{
    NSString *logoutFilePath = [[self.crashFilePath stringByDeletingPathExtension] stringByAppendingPathExtension:@"log"];
    NSError *error;
    [logOut writeToFile:logoutFilePath atomically:**YES** encoding:NSUTF8StringEncoding error:&error];
    if (error){
        dispatch_async(dispatch_get_main_queue(), ^{
            [self AlertViewShowWithMessageText:NSLocalizedString(@"alert", @"") InforMativeText:NSLocalizedString(@"errorWriteLog", @"")];
        });
    }
}

-(void)AlertViewShowWithMessageText:(NSString *)message InforMativeText:(NSString*)info{
    NSAlert *alert=[NSAlert alertWithMessageText:message defaultButton:NSLocalizedString(@"confirm",@"") alternateButton:NSLocalizedString(@"cancel",@"") otherButton:nil informativeTextWithFormat: info,nil];
    [alert beginSheetModalForWindow:[NSApplication sharedApplication].keyWindow completionHandler:nil];
}