• APP 运行时拥有防 Crash 功能能有效降低 Crash 率,提升 APP 稳定性
  • 开发程序的过程中不管我们已经如何小心,总是会在不经意间遇到程序闪退。流畅的操作被无情地Crash打断,当程序运行Crash的时候,系统会把运行的最后时刻的运行信息记录下来,存储到一个文件中,也就是我们...

    参考博文:http://www.cnblogs.com/easonoutlook/archive/2012/12/27/2835979.html


    开发程序的过程中不管我们已经如何小心,总是会在不经意间遇到程序闪退。流畅的操作被无情地Crash打断,当程序运行Crash的时候,系统会把运行的最后时刻的运行信息记录下来,存储到一个文件中,也就是我们所说的Crash文件,当时如果是真机测试离开Xcode的时候Crash掉,我们是无法知道crash的具体位置的。现在做一个程序统一记录crash的位置。先科普一下crash的知识:


    Crash文件结构

    1、Process Information(进程信息)

    Incident Idnetifier 崩溃报告的唯一标识符,不同的Crash
    CrashReporter Key 设备标识相对应的唯一键值(并非真正的设备的UDID,苹果为了保护用户隐私iOS6以后已经无法获取)。通常同一个设备上同一版本的App发生Crash时,该值都是一样的。
    Hardware Model 代表发生Crash的设备类型,上图中的“iPad4,4”代表iPad Air
    Process 代表Crash的进程名称,通常都是我们的App的名字, []里面是当时进程的ID
    Path 可执行程序在手机上的存储位置,注意路径时到XXX.app/XXX,XXX.app其实是作为一个Bundle的,真正的可执行文件其实是Bundle里面的XXX,感兴趣的可以自己查一下相关资料,有机会我后面也会介绍到
    Identifier 你的App的Indentifier,通常为“com.xxx.yyy”,xxx代表你们公司的域名,yyy代表某一个App
    Version 当前App的版本号,由Info.plist中的两个字段组成,CFBundleShortVersionString and CFBundleVersion
    Code Type 当前App的CPU架构
    Parent Process 当前进程的父进程,由于iOS中App通常都是单进程的,一般父进程都是launchd


    2、Basic Information

    Date/Time Crash发生的时间,可读的字符串
    OS Version 系统版本,()内的数字代表的时Bulid号
    Report Version Crash日志的格式,目前基本上都是104,不同的version里面包含的字段可能有不同

     

     

    3、Exception(非常重要)

    Exception Type 异常类型
    Exception Subtype: 异常子类型
    Crashed Thread 发生异常的线程号

     

     

     

    4、Thread Backtrace

    发生Crash的线程的Crash调用栈,从上到下分别代表调用顺序,最上面的一个表示抛出异常的位置,依次往下可以看到API的调用顺序。上图的信息表明本次Crash出现xxxViewController的323行,出错的函数调用为orderCountLoadFailed。

    5、Thread State

    Crash时发生时刻,线程的状态,通常我们根据Crash栈即可获取到相关信息,这部分一般不用关心。

    6、Binary Images

    Crash时刻App加载的所有的库,其中第一行是Crash发生时我们App可执行文件的信息,可以看出为armv7,可执行文件的包得uuid位c0f……cd65,解析Crash的时候dsym文件的uuid必须和这个一样才能完成Crash的符号化解析。


    废话说了那么多,到底怎么捕获整个系统的crash呢,入正题,其实也很简单:创建一个工具类  ExceptionHandler.h  和 ExceptionHandler.m(oc版本),swift版本的下面再讲.

    ExceptionHandler.h

    #import <UIKit/UIKit.h>


    @interface ExceptionHandler : NSObject{

    BOOL dismissed;

    }


    @end

    void HandleException(NSException *exception);

    void SignalHandler(int signal);



    void InstallUncaughtExceptionHandler(void);


    ExceptionHandler.m

    #import "ExceptionHandler.h"

    #include <libkern/OSAtomic.h>

    #include <execinfo.h>


    NSString * const ExceptionHandlerSignalExceptionName = @"UncaughtExceptionHandlerSignalExceptionName";

    NSString * const ExceptionHandlerSignalKey = @"UncaughtExceptionHandlerSignalKey";

    NSString * const ExceptionHandlerAddressesKey = @"UncaughtExceptionHandlerAddressesKey";


    volatile int32_t UncaughtExceptionCount = 0;

    const int32_t UncaughtExceptionMaximum = 10;


    const NSInteger ExceptionHandlerSkipAddressCount = 4;

    const NSInteger ExceptionHandlerReportAddressCount = 5;


    @implementation ExceptionHandler


    + (NSArray *)backtrace

    {

    void  * callstack[128];

        

    int frames = backtrace(callstack, 128);

         char **strs = backtrace_symbols(callstack, frames);

     

    int i;

    NSMutableArray *backtrace = [NSMutableArray arrayWithCapacity:frames];

    for (

    i = ExceptionHandlerSkipAddressCount;

    i < ExceptionHandlerSkipAddressCount +

    ExceptionHandlerReportAddressCount;

    i++)

    {

    [backtrace addObject:[NSString stringWithUTF8String:strs[i]]];

    }

    free(strs);

     

    return backtrace;

    }


    - (void)alertView:(UIAlertView *)anAlertView clickedButtonAtIndex:(NSInteger)anIndex

    {

    if (anIndex == 0)

    {

    dismissed = YES;

    }

    }


    - (void)validateAndSaveCriticalApplicationData

    {

    }


    - (void)handleException:(NSException *)exception

    {

    [self validateAndSaveCriticalApplicationData];

    UIAlertView *alert =

    [[UIAlertView alloc]

    initWithTitle:NSLocalizedString(@"Unhandled exception"nil)

    message:[NSString stringWithFormat:NSLocalizedString(

    @"You can try to continue but the application may be unstable.\n\n"

    @"Debug details follow:\n%@\n%@"nil),

    [exception reason],

    [[exception userInfo] objectForKey:ExceptionHandlerAddressesKey]]

    delegate:self

    cancelButtonTitle:NSLocalizedString(@"Quit"nil)

    otherButtonTitles:NSLocalizedString(@"Continue"nil), nil];

    [alert show];

        

        NSUserDefaults defaults = [NSUserDefaults standardUserDefaults];

        

    CFRunLoopRef runLoop = CFRunLoopGetCurrent();

    CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);

    while (!dismissed)

    {

    for (NSString *mode in (NSArray *)allModes)

    {

    CFRunLoopRunInMode((CFStringRef)mode, 0.001false);

    }

    }

    CFRelease(allModes);


    NSSetUncaughtExceptionHandler(NULL);

    signal(SIGABRT, SIG_DFL);

    signal(SIGILL, SIG_DFL);

    signal(SIGSEGV, SIG_DFL);

    signal(SIGFPE, SIG_DFL);

    signal(SIGBUS, SIG_DFL);

    signal(SIGPIPE, SIG_DFL);

    if ([[exception name] isEqual:ExceptionHandlerSignalExceptionName])

    {

    kill(getpid(), [[[exception userInfo] objectForKey:ExceptionHandlerSignalKey] intValue]);

    }

    else

    {

    [exception raise];

    }

    }


    @end


    void HandleException(NSException *exception)

    {

    int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);

    if (exceptionCount > UncaughtExceptionMaximum)

    {

    return;

    }

    NSArray *callStack = [ExceptionHandler backtrace];

    NSMutableDictionary *userInfo =

    [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];

    [userInfo

    setObject:callStack

    forKey:ExceptionHandlerAddressesKey];

    [[[ExceptionHandler alloc] init]

    performSelectorOnMainThread:@selector(handleException:)

    withObject:

    [NSException

    exceptionWithName:[exception name]

    reason:[exception reason]

    userInfo:userInfo]

    waitUntilDone:YES];

    }


    void SignalHandler(int signal)

    {

    int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);

    if (exceptionCount > UncaughtExceptionMaximum)

    {

    return;

    }

    NSMutableDictionary *userInfo =

    [NSMutableDictionary

    dictionaryWithObject:[NSNumber numberWithInt:signal]

    forKey:ExceptionHandlerSignalKey];


    NSArray *callStack = [ExceptionHandler backtrace];

    [userInfo

    setObject:callStack

    forKey:ExceptionHandlerAddressesKey];

    [[[ExceptionHandler alloc] init]

    performSelectorOnMainThread:@selector(handleException:)

    withObject:

    [NSException

    exceptionWithName:ExceptionHandlerSignalExceptionName

    reason:

    [NSString stringWithFormat:

    NSLocalizedString(@"Signal %d was raised."nil),

    signal]

    userInfo:

    [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:ExceptionHandlerSignalKey]]

    waitUntilDone:YES];

    }


    void InstallUncaughtExceptionHandler(void)

    {

    NSSetUncaughtExceptionHandler(&HandleException);

    signal(SIGABRT, SignalHandler);

    signal(SIGILL, SignalHandler);

    signal(SIGSEGV, SignalHandler);

    signal(SIGFPE, SignalHandler);

    signal(SIGBUS, SignalHandler);

    signal(SIGPIPE, SignalHandler);

    }


    使用方式:

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions{

        

        InstallUncaughtExceptionHandler();

        return YES;

    }



    大功告成


    swift版本:

    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObjectAnyObject]?) -> Bool {

              

            //输出bug信息

            print("系统bug日志记录---------------------\(NSUserDefaults.standardUserDefaults().valueForKey(ERROR_MESSAGE))")

            NSSetUncaughtExceptionHandler { exception in

                var message = exception.callStackSymbols

                message.removeAll()

                message.append("错误理由:\(exception.reason!)")

                message.append("错误详细信息:\(exception.callStackSymbols)")

                NSUserDefaults.standardUserDefaults().setObject(message, forKey: ERROR_MESSAGE)

                NSUserDefaults.standardUserDefaults().synchronize()

                print("系统bug日志记录---------------------\n\(message)")

            }

            return true

        }

        


    swift比oc简单很多,直接在didFinishLaunchingWithOptions加入上面的代码就可以。



    测试代码:

     NSMutableArray *array = [NSMutableArray array];

        [array addObject:@"1"];

        [array addObject:@"2"];

        

        NSLog(@"%@",array[9]);



    效果图  数组越界:

    wKiom1f_MpXSxOepAASpRFAeCaQ856.png-wh_50


    展开全文
  • 1. 为什么会Crash 常见的Crash原因有:访问已经被释放的内存,数组越界,使用!解包值为nil的变量。当遇到这些情况时,说明应用已经遇到了很严重的非预期错误,无法再继续运行。操作系统检测到这些非法操作时会向应用...

    1. 为什么会Crash

    常见的Crash原因有:访问已经被释放的内存,数组越界,使用!解包值为nil的变量。当遇到这些情况时,说明应用已经遇到了很严重的非预期错误,无法再继续运行。操作系统检测到这些非法操作时会向应用发送对应的信号,而应用对这些信号的默认处理是直接让应用退出(已信号值作为退出码)。这样就出现了我们看到的Crash,闪退。

    具体的信号种类和信号机制见Unix Signal

    2. 如何捕获和处理Crash

    在swift3.0中,我们可以通过如下调用来注册对特定信号的处理逻辑。

    signal(SIGINT, {s in print(s);})

    在处理函数中我们一般做如下操作:

    • 获取调用栈信息
    • 相关逻辑处理(比如保存调用栈到文件或数据库)
    • 恢复信号处理函数到默认设置
    • 调用exit退出应用

    3. 获取调用栈信息

    分析Crash最有价值的信息就是调用栈。在Swift 3.0中我们可以调用Thread.callStackSymbols来获取Crash的调用栈信息。(以前是通过backtrace和backtrace_symbols系统调用来获取)

    需要说明的是Thread.callStackSymbols返回的调用栈也包含了从crash触发代码到调用Thread.callStackSymbols之间的调用栈信息,可根据需要选择是否去除。

    基于以上技术点,我分别基于OC和Swift实现了两套CrashHandler,以及示例程序。代码见:https://github.com/lbwxly/CrashHandler

    转载于:https://www.cnblogs.com/Code-life/p/7103697.html

    展开全文
  • 假如你开发的app有这个需求的话:崩溃后自动重启。你可以参照下文:

         假如你开发的app有这个需求的话:崩溃后自动重启或者捕获异常信息。你可以参照下文:


    package com.tan.abnormalrestart;
    
    import java.lang.Thread.UncaughtExceptionHandler;
    
    import android.app.Application;
    import android.content.Intent;
    
    public class AppContext extends Application
    {
    	protected static AppContext instance;
    	public void onCreate() {
    		super.onCreate();
    		instance = this;
    		Thread.setDefaultUncaughtExceptionHandler(restartHandler); // 程序崩溃时触发线程  以下用来捕获程序崩溃异常  
    	}
    	// 创建服务用于捕获崩溃异常  
        private UncaughtExceptionHandler restartHandler = new UncaughtExceptionHandler() {  
            public void uncaughtException(Thread thread, Throwable ex) {  
                restartApp();//发生崩溃异常时,重启应用  
            }  
        };  
        public void restartApp(){
        	Intent intent = new Intent(instance,MainActivity.class);
    		intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
    		instance.startActivity(intent);
    		android.os.Process.killProcess(android.os.Process.myPid());  //结束进程之前可以把你程序的注销或者退出代码放在这段代码之前
        }
    }
    

    这个是application级别的应用!
    其实关键还是UncaughtExceptionHandler类!各位自己百度google了!

    展开全文
  • Swift开发Mac App(6)

    2015-05-07 09:28:47
    接下来实现EDStarRating 的编辑。 在loadView 方法中,我们已经配置了...打开MasterViewController.swift 在 EDStarRatingProtocol 扩展中添加如下方法: func starsSelectionChanged(control: EDStarRating!,

    接下来实现EDStarRating 的编辑。 在loadView 方法中,我们已经配置了EDStarRating的delegate属性,我们仅仅需要实现相关委托方法即可。

    打开MasterViewController.swift 在 EDStarRatingProtocol 扩展中添加如下方法:

    func starsSelectionChanged(control: EDStarRating!, rating: Float) {

       if let selectedDoc = selectedBugDoc() {

    selectedDoc.data.rating = Double(self.bugRating.rating)

       }

    }

    跟前面几乎一样: 获得用户选定的昆虫模型,用修改后的值赋值给它。

    运行程序。需要注意的是,用户设定新的评级后这个值是被持久化的,哪怕你切换到其他昆虫然后有切换回来。


    获取本地图片

    打开 MasterViewController.xib,拖一个“Push Button” 控件到image view下方。

    修改按钮的title 为 “Change Picture”:


    如同加号按钮和减号按钮,为Change Picture 按钮创建一个IBAction,命名为 changePicture

    这个action在按钮点击时调用。

    OS X 有一个特有的控件叫做 IKPictureTaker,允许用户从计算机上选择一张图片,或者从摄像头捕捉一张图片。

    当用户选择了图片之后,这个控件会调用指定的delegate方法。

    打开MasterViewController.swift 加入以下import 语句:

    import Quartz

    这个 image picker属于 Quartz 框架。

    changePicture方法中,添加代码:

    if let selectedDoc = selectedBugDoc() {

       IKPictureTaker().beginPictureTakerSheetForWindow(self.view.window,

    withDelegate: self,

         didEndSelector: "pictureTakerDidEnd:returnCode:contextInfo:",

         contextInfo: nil)

    }

    我们先检查用户是否选择了有效的昆虫,如果是,显示picture taker控件。

    然后实现pictureTakerDidEnd(_:returnCode:contextInfo:)方法:

    func pictureTakerDidEnd(picker: IKPictureTaker, returnCode: NSInteger, contextInfo: UnsafePointer<Void>) {

       let image = picker.outputImage()

       if image != nil && returnCode == NSOKButton {

    self.bugImageView.image = image

    if let selectedDoc = selectedBugDoc() {

           selectedDoc.fullImage = image

           selectedDoc.thumbImage = image.imageByScalingAndCroppingForSize(CGSize(width: 44, height: 44))       reloadSelectedBugRow()

    }  

       }

    }

    首先检查用户是否点击了OK (NSOKButton) 以及选择的图片是否有效。

    如果是,获取用户选定的昆虫模型,修改昆虫的图片及缩略图,然后更新cell。

    运行程序,选择一个昆虫,点击Change Picture, 从本地文件或摄像头中获取一张图片,这张图片将立即在选定的cell中得到更新。

    一些细节上的问题

    当你运行程序,视图改变窗口大小,你会发现控件并不能自动适应大小。


    这是窗口拖大后的效果。


    pplns:o="urn:schemas-microsoft-com:office:office"xmlns:w="urn:schemas-microsoft-com:office:word"xmlns:m="http://schemas.microsoft.com/office/2004/12/omml"xmlns="http://www.w3.org/TR/REC-html40">

    这是窗口缩小后的效果。

    另外,我们还没有为App数据进行持久化。一旦App重启,用户对数据进行的增加和修改都会丢失。

    打开MasterViewController.xib,将View的Size缩小至最小能够足以显示所有控件的程度。


    在上图中,3个按钮放在了同一排。在右边细节展示区域中,所有的控件都左对齐,且宽度一致(除了ChangePicture按钮)。

    然后,我们在中间增加一个分割线。拖一个 VerticalLine 到View的中央。


    复原操作

    复原操作用于将数据恢复至原来的状态。拖一个Push 按钮在Table View下方,修改其标题为Reset。然后打开Assistant Editor,为按钮创建一个IBAction,名为resetData(确认当前打开的源文件为MasterViewController.swift )。


    resetData()方法加入如下代码:

    setupSampleBugs() 
    updateDetailInfo(nil) 
    bugsTableView.reloadData()

    setupSampleBugs() 方法调用会恢复所有模型数据。 以nil作为参数值调用updateDetailInfo 方法将清除所有细节字段。然后刷新Table View。

    运行程序,添加、删除或修改任意数据。然后点击Reset按钮,所有数据又恢复原样。


    展开全文
  • 1.前言 开发iOS应用,解决Crash问题始终是一个难题。Crash分为两种,一种是由EXC_BAD_ACCESS引起的,原因是访问了不属于本进程...其实对于未捕获的Objective-C异常,我们是有办法将它记录下来的,如果日志记录得当,能

    1.前言

    开发iOS应用,解决Crash问题始终是一个难题。Crash分为两种,一种是由EXC_BAD_ACCESS引起的,原因是访问了不属于本进程的内存地址,有可能是访问已被释放的内存;另一种是未被捕获的Objective-C异常(NSException),导致程序向自身发送了SIGABRT信号而崩溃。其实对于未捕获的Objective-C异常,我们是有办法将它记录下来的,如果日志记录得当,能够解决绝大部分崩溃的问题。这里对于UI线程与后台线程分别说明。

    网上的捕获异常的方法都大同小异,都是处理处理signal信号。这里我把捕获的异常保存到缓存目录上,然后你可以通过邮件或者网络接口把自己发给自己或者公司,便于自己分析和在下个版本上进行修复工作。

    2.创建单利

    单利主要做有一下功能,捕获崩溃日志、存取日子和界面提示功能;详见ZFJUncaughtExceptionHandler.h的方法;

    #import <Foundation/Foundation.h>
    
    //返回地址路径
    typedef void(^ logPathBlock)(NSString *pathStr);
    
    @interface ZFJUncaughtExceptionHandler : NSObject
    
    + (instancetype)shareInstance;
    
    @property (nonatomic,copy) logPathBlock pathBlock;
    
    //是否显示错误提示框 默认是不显示的
    @property (nonatomic, copy) ZFJUncaughtExceptionHandler*(^showAlert)(BOOL yesOrNo);
    
    //是否显示错误信息
    @property (nonatomic, copy) ZFJUncaughtExceptionHandler*(^showErrorInfor)(BOOL yesOrNo);
    
    //回调返回错误日志
    @property (nonatomic, copy) ZFJUncaughtExceptionHandler*(^getlogPathBlock)(void(^ logPathBlock)(NSString *pathStr));
    
    //错误日志路径
    @property (nonatomic,strong) NSString *logFilePath;
    
    ZFJUncaughtExceptionHandler * InstanceZFJUncaughtExceptionHandler(void);
    
    @end
    初始化

    + (instancetype)shareInstance{
        static ZFJUncaughtExceptionHandler *_manager = nil;
        static dispatch_once_t oncePredicate;
        dispatch_once(&oncePredicate, ^{
            _manager = [[self alloc] init];
            [_manager uiConfig];
        });
        return _manager;
    }
    
    #pragma mark - 设置日志存取的路径
    - (void)uiConfig{
        NSString *docPath = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) lastObject];
        NSString *filePath = [docPath stringByAppendingPathComponent:@"ZFJUncaughtExceptionHandlerLog.txt"];
        NSFileManager *fileManager = [NSFileManager defaultManager];
        if (![fileManager fileExistsAtPath:filePath]) {
            [fileManager createFileAtPath:filePath contents:[@"~~~~~~~~~~~~~~~~~~程序异常日志~~~~~~~~~~~~~~~~~~\n\n" dataUsingEncoding:NSUTF8StringEncoding] attributes:nil];
        }
        self.logFilePath = filePath;
    }
    
    默认我是打开异常捕获的,当然你可以这这里面添加一个字段来控制打开或者关闭捕获功能;

    ZFJUncaughtExceptionHandler *InstanceZFJUncaughtExceptionHandler(void){
        NSSetUncaughtExceptionHandler(&HandleException);
        signal(SIGABRT, SignalHandler);
        signal(SIGILL, SignalHandler);
        signal(SIGSEGV, SignalHandler);
        signal(SIGFPE, SignalHandler);
        signal(SIGBUS, SignalHandler);
        signal(SIGPIPE, SignalHandler);
        return [ZFJUncaughtExceptionHandler shareInstance];
    }
    可以通过添加一个字段install来打开或者关闭,例如:
    NSSetUncaughtExceptionHandler(install ? HandleException : NULL);
        signal(SIGABRT, install ? SignalHandler : SIG_DFL);
        signal(SIGILL, install ? SignalHandler : SIG_DFL);
        signal(SIGSEGV, install ? SignalHandler : SIG_DFL);
        signal(SIGFPE, install ? SignalHandler : SIG_DFL);
        signal(SIGBUS, install ? SignalHandler : SIG_DFL);
        signal(SIGPIPE, install ? SignalHandler : SIG_DFL);
    从异常堆栈里面获取异常信息;

    void HandleException(NSException *exception){
        int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
        //如果太多不用处理
        if (exceptionCount > UncaughtExceptionMaximum) {
            return;
        }
        //获取调用堆栈
        NSArray *callStack = [exception callStackSymbols];
        NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithDictionary:[exception userInfo]];
        [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];
        //在主线程中,执行制定的方法, withObject是执行方法传入的参数
        [[ZFJUncaughtExceptionHandler shareInstance] performSelectorOnMainThread:@selector(handleException:) withObject:[NSException exceptionWithName:[exception name] reason:[exception reason] userInfo:userInfo] waitUntilDone:YES];
    }
    
    void SignalHandler (int signal){
        int32_t exceptionCount = OSAtomicIncrement32(&UncaughtExceptionCount);
        if (exceptionCount > UncaughtExceptionMaximum) {
            return;
        }
        NSMutableDictionary *userInfo = [NSMutableDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey];
        NSArray *callStack = [ZFJUncaughtExceptionHandler backtrace];
        [userInfo setObject:callStack forKey:UncaughtExceptionHandlerAddressesKey];
        [[ZFJUncaughtExceptionHandler shareInstance] performSelectorOnMainThread:@selector(handleException:) withObject: [NSException exceptionWithName:UncaughtExceptionHandlerSignalExceptionName reason: [NSString stringWithFormat: NSLocalizedString(@"Signal %d was raised.", nil), signal] userInfo: [NSDictionary dictionaryWithObject:[NSNumber numberWithInt:signal] forKey:UncaughtExceptionHandlerSignalKey]] waitUntilDone:YES];
    }
    处理异常

    - (void)handleException:(NSException *)exception{
        //保存日志 可以发送日志到自己的服务器上
        [self validateAndSaveCriticalApplicationData:exception];
        NSString *_erroeMeg = nil;
        NSString *userInfo = [[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey];
        if(self.isShowErrorInfor){
            _erroeMeg = [NSString stringWithFormat:NSLocalizedString(@"如果点击继续,程序有可能会出现其他的问题,建议您还是点击退出按钮并重新打开\n" @"异常原因如下:\n%@\n%@", nil), [exception reason], userInfo];
        }else{
            _erroeMeg = [NSString stringWithFormat:NSLocalizedString(@"如果点击继续,程序有可能会出现其他的问题,建议您还是点击退出按钮并重新打开", nil)];
        }
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"抱歉,程序出现了异常" message:_erroeMeg delegate:self cancelButtonTitle:@"退出" otherButtonTitles:@"继续", nil];
        dispatch_async(dispatch_get_main_queue(), ^{
            if(_isShowAlert){
                [alert show];
            }
        });
    
        CFRunLoopRef runLoop = CFRunLoopGetCurrent();
        CFArrayRef allModes = CFRunLoopCopyAllModes(runLoop);
        while (!_dismissed){
            for (NSString *mode in (__bridge NSArray *)allModes) {
                CFRunLoopRunInMode((CFStringRef)mode, 0.001, false);
            }
        }
        CFRelease(allModes);
    #pragma clang diagnostic pop
        NSSetUncaughtExceptionHandler(NULL);
        signal(SIGABRT, SIG_DFL);
        signal(SIGILL, SIG_DFL);
        signal(SIGSEGV, SIG_DFL);
        signal(SIGFPE, SIG_DFL);
        signal(SIGBUS, SIG_DFL);
        signal(SIGPIPE, SIG_DFL);
        if ([[exception name] isEqual:UncaughtExceptionHandlerSignalExceptionName]) {
            kill(getpid(), [[[exception userInfo] objectForKey:UncaughtExceptionHandlerSignalKey] intValue]);
        }else{
            [exception raise];
        }
    }
    异常日志保存到本地;

    #pragma mark - 保存错误信息日志
    - (void)validateAndSaveCriticalApplicationData:(NSException *)exception{
        NSString *exceptionMessage = [NSString stringWithFormat:NSLocalizedString(@"\n******************** %@ 异常原因如下: ********************\n%@\n%@\n==================== End ====================\n\n", nil), [self currentTimeString], [exception reason], [[exception userInfo] objectForKey:UncaughtExceptionHandlerAddressesKey]];
        NSFileHandle *handle = [NSFileHandle fileHandleForUpdatingAtPath:self.logFilePath];
        [handle seekToEndOfFile];
        [handle writeData:[exceptionMessage dataUsingEncoding:NSUTF8StringEncoding]];
        [handle closeFile];
        if(self.pathBlock){
            self.pathBlock(self.logFilePath);
        }
    }

    3.函数调用

    - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
        // Override point for customization after application launch.
    
        //简单调用
        //InstanceZFJUncaughtExceptionHandler();
    
        //链式调用 是否显示警告框 是否显示错误信息 是否回调日志地址
        InstanceZFJUncaughtExceptionHandler().showAlert(YES).showErrorInfor(YES).getlogPathBlock(^(NSString *logPathStr){
            NSLog(@"程序异常日志地址 == %@",logPathStr);
        });
        
        //当然我们也可以直接直接获取日志文件地址
        //NSString *logFilePath = InstanceZFJUncaughtExceptionHandler().logFilePath;
        
        return YES;
    }

    4.运行效果

    5.DEMO下载

    点击下载

    http://download.csdn.net/detail/u014220518/9705941













    展开全文
  • Swift实现一款天气预报APP(一) 用Swift实现一款天气预报APP(二) 用Swift实现一款天气预报APP(三)   通过前面的学习,一个天气预报的APP已经基本可用了。至少可以查看现在当前的天气情况和未来几个小时...
  • 神奇的 Swift 错误处理

    2016-08-01 10:54:02
    原文:Magical Error Handling in Swift ...重要的改变发生在 Swift 2,它率先使用了“处理非异常的状态和条件”的做法,使你的 app 变得更加简单。类似于其它编程语言,在 Swift 中,选择使用哪种错误处理技
  • swift语言编写一个简单的条形码扫描APP原文地址:appcoda 在处理职员在杂货店的收银台排了很长的队伍,在机场帮助检查背包和旅客,或者在主要的食品供应商,协助处理乏味的存货清单过程,条形码扫描是很简单的处理...
  • 前言 hihi,勇敢的小伙伴儿们大家好, 了解...记得以前看安卓代码有try/catch的捕捉异常和处理异常的操作(此处存疑,记不太清了),想着iOS异常是怎么捕捉到的呢?于是有了这篇博客。 接下来跟大家一起学习一下N...
  • Swift 中的错误与异常

    2019-02-28 11:21:09
    本文为学习笔记,对错误和异常的处理在我个人的代码中做的比较烂...而 Swift中有非常有些的处理错误机制,故将自己的学习记录成文. ###反惯例,先上参考文章: ####1.喵神的 Swifter必备 tips 第三版 ####2.Objc.cn ###...
  • <p>I have already turned on "Allow less secure app" in my Google account, but it's not the error code "535", so I don't think Google security check is the problem here. <p>Any ways to fix this ...
  • Swift 开源项目汇总

    2019-09-16 16:07:05
    Swift 开源项目汇总
  • 在状态栏上显示 FPS,CPU和内存使用率,更好的监控应用性能 
  • Fatal error: Uncaught exception 'Swift_TransportException' with message ' in C:\...\vendor\swiftmailer\swiftmailer\lib\classes\Swift\Transport\StreamBuffer.php on line 266 --- Swift_TransportException...
  • 最近使用NSSetUncaughtExceptionHandler和signal方法捕获异常,但是捕获到的都是堆栈信息 ,如何利用这些堆栈信息查看报错方法名和行数?
  • 近日,苹果开发者博客更新了一篇关于Swift 5的文章,带来了Swift 5新特性的消息,其中最受开发期待的莫过于iOS 12.2将带来ABI 稳定性,这意味着基础库将植入系统中,不再包含在App中,应用程序的体积会更小,更多新...
1 2 3 4 5 ... 20
收藏数 2,302
精华内容 920