iOS 错误处理 (二)

来源:互联网 发布:个人所得税申报软件 编辑:程序博客网 时间:2024/05/22 03:50

2.   捕获与报告程序崩溃

   iTunes Connet 可以提供crash报告,但是他有很多限制。苹果只会向用户征求一次意见,如果得到用户容许,应用以后就可以上传crash报告了。不过多数用户会拒绝,而且每天只能上传一次报告。iTunesConnet只对App Store中得应用提供错误报告。所以,在开发和测试期间需要使用其他系统管理crash报告。总之,如果iTunes Connet能够满足你得需求,那很好,不过通常是无法满足的。

    最好的iTunes Connet 替代品是Quincy Kit(http://quincykit.net)

HockeyApp(http://hockeyapp.net)中就继承了QuincyKit。可以很方便的把Quincy Kit集成到已有的工程中,得到用户容许后,就可以将报告上传到HockeyApp得服务器上,也可以上传到自己的服务器上。目前它只上传crash报告,不上传日志。

错误报告中可能会有一些符号,也可以没有,这取决于错误报告生成方式。只要保留这二进制程序的.dSYM 文件。Xcode就可以非常好的把报告符号化(用方法名取代内存地址)。Xcode通过spotlight查找这些文件,所以要确保这些文件可以被spotlight找到。如果使用了HockeyApp管理crash报告,也可以吧符号化文件上传到HockeyApp。

在XCODE编译项目之后,会在app旁看见一个同名的dSYM文件.
他是一个编译的中转文件,简单说就是debug的symbols包含在这个文件中.他有什么作用? 当release的版本 crash的时候,会有一个日志文件,包含出错的内存地址, 使用symbolicatecrash工具能够把日志和dSYM文件转换成可以阅读的log信息,也就是将内存地址,转换成程序里的函数或变量和所属于的文件名.

3.   错误和NSError

错误分为两种  

        a 程序错误  程序错误是在调试模式中需要处理异常,在发布模式中则需要记录错误日志。在发布模式中如果出现可能造成数据污染的程序错误,也需要抛出异常。iOS中分配小块内存失败应该是为程序错误。这几乎不可能发生。如果发生了,那么肯定是程序错误。

        b 用户错误: 网络连接失败,磁盘空间不足等等

           不应该抛出异常,应该返回错误信息,通常使用NSError对象来返回错误信息。NSFileManager 经常会用到NSError。

 

  NSError* error = nil;

  if(![NSFileManager defaultManager] copyItemAtPath:srcPath toPath:toPatherror:&error)]) {

        [selfhandleError:error];

}

这个方法可以吧一个文件复制到另外一个地方。很明显,有很多原因可能会导致这个方法执行失败。失败时返回NO,同时更新NSError对象,进而吧错误信息返回给调用者。

代码剖析(看起来会是这个样子):

- (BOOL)copyItemAtPath:(NSString *)srcPathtoPath:(NSString *)dstPath error:(NSError **)error NS_AVAILABLE(10_5, 2_0)

{

    BOOL success =---------;

    if(! success)

    {

        if(error !=NULL){

            * error= [NSError errorWithDomain:-----------]

        }

    }

    return success;

}    

     error 是指向指针的指针。要对它进行检查,在对error解引用之前需要确保它不为NULL。如果调用者并不关心出错细节,就可以直接传入一个NULL值。还需要对返回值检查,确保所有操作都是成功的。

// Predefined domain for errors from most AppKit andFoundation APIs.

// AppKitFoundation库中主要的错误域

FOUNDATION_EXPORT NSString *constNSCocoaErrorDomain; 

// Other predefined domains;value of "code" will correspond to preexisting values in these domains.其他域

FOUNDATION_EXPORT NSString *const NSPOSIXErrorDomain;

FOUNDATION_EXPORT NSString *const NSOSStatusErrorDomain;

FOUNDATION_EXPORT NSString *const NSMachErrorDomain;

      NSError对象中,主要有三个私有变量

       错误域(NSInteger): _domain

       错误标示(NSString *):_code

       错误详细信息(NSDictionary *):_userInfo

       通常用_domain和_code一起标识一个错误信息

     对应三个方法:

- (NSString *)domain;

- (NSInteger)code;

- (NSDictionary *)userInfo;

     

NSError中用户信息字典可以用来存储任何信息。这个字典中有很多预定义的键。

      // Keysin userInfo, for subsystems wishing to provide their error messages up-front.

FOUNDATION_EXPORT NSString *constNSLocalizedDescriptionKey;  // NSString

FOUNDATION_EXPORT NSString *constNSLocalizedFailureReasonErrorKey    ;  // NSString

FOUNDATION_EXPORT NSString *constNSLocalizedRecoverySuggestionErrorKey; // NSString

FOUNDATION_EXPORT NSString *constNSLocalizedRecoveryOptionsErrorKey  ;  // NSArray of NSStrings

FOUNDATION_EXPORT NSString *constNSRecoveryAttempterErrorKey;  // Instanceof a subclass of NSObject that conforms to the NSErrorRecoveryAttemptinginformal protocol

FOUNDATION_EXPORT NSString *constNSHelpAnchorErrorKey               ;  // NSString containing a helpanchor

 

// Other standard keys in userInfo, for various errorcodes

FOUNDATION_EXPORT NSString *constNSStringEncodingErrorKey ;  // NSNumbercontaining NSStringEncoding

FOUNDATION_EXPORT NSString *const NSURLErrorKey          ;  // NSURL

FOUNDATION_EXPORT NSString *const NSFilePathErrorKey     ;  // NSString

     一些关于键的使用:

详细描述键

NSString *const NSLocalizedDescriptionKey;

取方法

- (NSString *)localizedDescription;

失败原因键

NSString *constNSLocalizedFailureReasonErrorKey

取方法

- (NSString *)localizedFailureReason;

恢复建议键

NSString *const NSLocalizedRecoverySuggestionErrorKey;

取方法

- (NSString *)localizedRecoverySuggestion;

恢复选项键

NSString *constNSLocalizedRecoveryOptionsErrorKey

取方法

- (NSArray *)localizedRecoveryOptions;

    

4.   错误处理块(我没有用过)

    块提供了一种非常灵活的错误处理方式。在异步操作中,传入一个错误处理快是非常有用的。也可以把错误处理块用于错误处理。

(如果要想深入了解错误相应与错误恢复,可以看看 Error Handing Programming guide)

 

  通常,块可以很方便的用于错误处理。NSFilePresenter 协议中有这样一个方法:

这是一个使用块进行错误处理的例子。这个方法可能会做一些费时间的操作,因此立即将执行结果通知给调用者是不现实的。这个方法的实现可能类似于如下所示:

- (void)savePresentedItemChangesWithCompletionHandler:                  (void (^)(NSError*errorOrNil))completionHandler   

 {      

   dispatch_queue_t queue =...;      

 //将请求分发到后台队列然后立即返回    

   dispatch_async(queue, ^{            

 //……执行一些操作……               

if (completionHandler) {          

          NSError *error = nil;         

          if(anErrorOccurred) {     

              error = [NSError errorWithDomain:...];    

            }         

                           //在主线程中执行完整的错误处理程序          

                         dispatch_sync(dispatch_get_main_queue(),^{           

completionHandler(error);        

});       

 }      

});   

 }

相对于使用委托回调方法进行错误处理,这种模式对于调用者来说更加方便。调用者能够把错误处理代码跟调用代码放在一起,而不必定义类似filePresenter:didFailWithError:这样的委托方法。前面的方法可能会被这样使用:

 

 [presentersavePresentedItemChangesWithCompletionHandler:^(NSError *e) {   

   if (e)

   {        

  ... respond to error ...    

  }    

  else{        

  ... cleanup after success ...  

   }

};

  前面的代码并没有把completionHandler存储到实例变量中,这样可以避免出现循环保留。在操作结束之前,可能会短暂地存在循环保留,操作结束之后就没有了。当操作结束并且错误处理被触发之后,循环保留就会立即被清理掉。

5.   日志 恰当的记录日志

Foundation 中有一个独立的日志函数 NSLog,非常不灵活,且效率低下。唯一优点:使用方便。这个函数将日志数输出到控制台,这对release版本而言根本没用,所以绝对不要在release版本中使用NSLog。

#ifdef DEBUG

#define MYLog NSLog

#else

#define MYLog

#endif

         这段代码会把release版本中得NSLog 全部移除,但是这样一来就完全没有了日志,所以不可取。你需要一个能够同时在开发环境和生产环境中使用的日志引擎。

    注意

1) 开发环境中,日志写入控制台;release版本,应该将日志写入文件。当然debug版本最好是两者都写入。

2) 应该分为多种不同的日志级别(错误,警告,信息,详细)

3) 当某个日志函数级别禁用时,相应日志函数的调用开销要非常小。(不懂)

4) 想控制台或文件写日志时,不可以阻塞调用者的线程

5) 要定期删除日志文件以避免占满磁盘空间

 获取日志文件

1)  可以通过网络协议让用户上传日志

2)  MFMailComposeViewController将日志以附件的形式通过电子邮件发送

日志文件可能非常大,通常在发送之前进行压缩以减小体积。可以使用Minizip对日志进行压缩。它提供多种压缩方法,比如Objective-Zip和ZipArchive。

TestFlight(http://testflightapp.com)支持把用户日志上传服务器,只需要在代码中使用TFLog()替代NSLog就可以了。Hockey 目前不支持上传日志。

 要先经过用户容许才可以发送日志。因为不但牵涉隐私问题,还有可能耗费大量的网络流量和电池流量。通常只有在需要查看问题报告是才有必要发送日志。

 Lumberjack  log 日志 http://github.com/robbiehanson/CocoaLumberjack

苹果文档: 1.Exception programming Topics

                       2. Error Handling Programming guide

                       3. Understanding and analyzing iphone osapplication crash reports