一、Objective - C 中的断言:

  • Objective - C 中的断言处理使用的是 NSAssertionHandler :

    每个线程拥有它自己的断言处理器,它是 NSAssertionHandler 类的实例对象。当被调用时,一个断言处理器打印一条包含方法和类名(或者函数名)的错误信息。然后它抛出一个 NSInternalInconsistencyException 异常。

  • 基础类中定义了两套断言宏
    • NSAssert / NSCAssert

      /** NSAssert */
      #if !defined(_NSAssertBody)
      #define NSAssert(condition, desc, ...) \
      do { \
      __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \
      if (!(condition)) { \
      NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \
      __assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \
      [[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \
      object:self file:__assert_file__ \
      lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \
      } \
      __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \
      } while(0)
      #endif
      /** NSCAssert */
      #if !defined(_NSCAssertBody)
      #define NSCAssert(condition, desc, ...) \
      do { \
      __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \
      if (!(condition)) { \
      NSString *__assert_fn__ = [NSString stringWithUTF8String:__PRETTY_FUNCTION__]; \
      __assert_fn__ = __assert_fn__ ? __assert_fn__ : @"<Unknown Function>"; \
      NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \
      __assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \
      [[NSAssertionHandler currentHandler] handleFailureInFunction:__assert_fn__ \
      file:__assert_file__ \
      lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \
      } \
      __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \
      } while(0)
      #endif
    • NSParameterAssert / NSCParameterAssert
      /** NSParameterAssert */
      #define NSParameterAssert(condition) NSAssert((condition), @"Invalid parameter not satisfying: %@", @#condition)
      /** NSCParameterAssert */
      #define NSCParameterAssert(condition) NSCAssert((condition), @"Invalid parameter not satisfying: %@", @#condition)
  • 这么做的意义在于两点:
    • 第一个是苹果对于断言处理在 API 层面进行了区分:

      • NSAssert 与 NSCAssert 用来处理一般情况的断言
      • NSParameterAssert 与 NSCParameterAssert 用来处理参数化的断言
    • 第二个是区别是在 Objective - C 和 C 之间进行了区分这样才有了:
      • NSAssert 与 NSCAssert
      • NSParameterAssert 与 NSCParameterAssert

二、使用 NSAssertionHandler

  • 从 Xcode 4.2 开始,发布构建默认关闭了断言,它是通过定义 NS_BLOCK_ASSERTIONS 宏实现的。也就是说,当编译发布版时,任何调用 NSAssert 等的地方都被有效的移除了。
    尽管基础类库的断言宏在它们自己的权力下十分有用————虽然只用于开发之中。NSAssertionHandler 还提供了一套优雅地处理断言失败的方式来保留珍贵的现实世界的使用信息。

Pay Attension:

据说,许多经验丰富的 Objective-C 开发者们告诫不要在生产环境中使用 NSAssertionHandler。基础类库中的断言处理是用来在一定安全距离外来理解和感激的。请小心行事如果你决定在对外发布版的应用中使用它。

  • NSAssertionHandler 是一个很直接的类,带有两个需要在子类中实现的方法:-handleFailureInMethod:... (当 NSAssert / NSParameterAssert 失败时调用)和 -handleFailureInFunction:... (当 NSCAssert / NSCParameterAssert 失败时调用)。
  • 接下来看一个使用的实例
    #pragram 第一步,创建一个继承自NSAssertionHandler 的类:LoggingAssertionHandler 用来专门处理断言
    #import <Foundation/Foundation.h>
    @interface LoggingAssertionHandler : NSAssertionHandler
    @end
    #import "LoggingAssertionHandler.h"
    @implementation LoggingAssertionHandler
    /** 重写两个失败的回调方法,在这里执行我们想要抛出的错误(打印或者直接报错) */
    - (void)handleFailureInMethod:(SEL)selector object:(id)object file:(NSString *)fileName lineNumber:(NSInteger)line description:(NSString *)format, ...{
    NSLog(@"NSAssert Failure: Method %@ for object %@ in %@#%li", NSStringFromSelector(selector), object, fileName, (long)line);
    NSException *e = [NSException
    exceptionWithName: NSStringFromSelector(selector)
    reason: format
    userInfo: nil];
    @throw e;
    }
    - (void)handleFailureInFunction:(NSString *)functionName file:(NSString *)fileName lineNumber:(NSInteger)line description:(NSString *)format, ...{
    NSLog(@"NSCAssert Failure: Function (%@) in %@#%li", functionName, fileName, (long)line);
    }
    @end
  • 每个线程都可以指定断言处理器。 想设置一个 NSAssertionHandler 的子类来处理失败的断言,在线程的threadDictionary 对象中设置 NSAssertionHandlerKey 字段即可。

    大部分情况下,你只需在

    -application:didFinishLaunchingWithOptions:

    中设置当前线程的断言处理器。

  • AppDelegate 中的处理
    - (BOOL)application:(UIApplication *)application
    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
    {
    NSAssertionHandler *assertionHandler = [[LoggingAssertionHandler alloc] init];
    [[[NSThread currentThread] threadDictionary] setValue:assertionHandler
    forKey:NSAssertionHandlerKey];
    // ...
    return YES;
    }
  • 这样我们就完成再当前线程中使用我们自定义的断言处理器的配置,那么接下来,如果有和我们条件不同的情况都直接会回调对应着的那两个失败的方法,我们可以在那俩个方法中按自己的输出意愿来处理你的话术。
  • 具体应用
    #import "ViewController.h"
    @interface ViewController ()
    @end
    @implementation ViewController
    - (void)viewDidLoad {
    [super viewDidLoad];
    NSObject*mc = [NSObject new];
    mc = @2;
    NSAssert(mc == nil, @"我不为空了");
    }
    @end

    根据输出情况可以看到是完全按照我们所需要的来输出的

    2015-10-30 21:33:14.529 NSAssert[20537:678428] ***
    Terminating app due to uncaught exception 'viewDidLoad', reason: '我不为空了'

三、使用上的注意点

  • 仔细观察 NSAssert 的宏定义 ,你会发现 self 的痕迹,有 self 的地方就一定要注意 block 容易产生的循环引用问题。

    /** NSAssert */
    #if !defined(_NSAssertBody)
    #define NSAssert(condition, desc, ...) \
    do { \
    __PRAGMA_PUSH_NO_EXTRA_ARG_WARNINGS \
    if (!(condition)) { \
    NSString *__assert_file__ = [NSString stringWithUTF8String:__FILE__]; \
    __assert_file__ = __assert_file__ ? __assert_file__ : @"<Unknown File>"; \
    [[NSAssertionHandler currentHandler] handleFailureInMethod:_cmd \
    object:self file:__assert_file__ \
    lineNumber:__LINE__ description:(desc), ##__VA_ARGS__]; \
    } \
    __PRAGMA_POP_NO_EXTRA_ARG_WARNINGS \
    } while(0)
    #endif
  • 接下来举个例子:
    /** 创建一个 preson 类 */
    #import <Foundation/Foundation.h>
    typedef void(^mitchelBlock)(int num);
    @interface person : NSObject
    @property(nonatomic, copy)mitchelBlock block;
    @end
    #import "person.h"
    @implementation person
    - (instancetype)init{
    if (self = [super init]) {
    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, (int64_t)(2 * NSEC_PER_SEC)), dispatch_get_main_queue(), ^{
    if (self.block) {
    self.block(1);
    }
    });
    }
    return self;
    }
    @end
    /** ViewController 中的代码 */
    #import "ViewController.h"
    #import "person.h"
    @interface ViewController ()
    @property(nonatomic, strong)person * aPerson;
    @end
    @implementation ViewController
    - (void)viewDidLoad {
    [super viewDidLoad];
    NSObject*mc = [NSObject new];
    mc = @2;
    self.aPerson = [person new];
    self.aPerson.block = ^(int num){
    NSAssert(mc == nil, @"我不为空了");
    NSLog(@"%d",num);
    };
    }
    @end

    这样我们就会看到 Block 中循环引用的警告啦:

    屏幕快照 2015-10-30 下午9.48.17.png

    那如果我想在 Block 中使用断言怎么办呐?用 NSCAssert 替换 NSAssertNSCParameterAssert 来替换 NSParameterAssert

    - (void)viewDidLoad {
    [super viewDidLoad];
    NSObject*mc = [NSObject new];
    mc = @2;
    self.aPerson = [person new];
    self.aPerson.block = ^(int num){
    NSCAssert(mc == nil, @"我不为空了");
    NSCParameterAssert(num>5);
    };
    }
  • 这样就 OK 了。

iOS - 断言处理与调试的更多相关文章

  1. 【ionic】Mac IOS下真机调试

    模拟调试不能保证真机一定没问题,所以真机调试是非常必要的一步 IOS设备 启用设备调试 在IOS设备中(Iphone,Ipad)中开始web检测器 设备->safari->高级->w ...

  2. iOS中web app调试(mac)

    原文 iOS中web app调试(mac).md 目录 一.真机联调配置 二.mac上Safari配置及真机联调 三.iOS模拟器使用 四.在iOS模拟器中安装app 近期公司vue项目开发,目的是一 ...

  3. ios申请真机调试( xcode 5)详细解析

    已经有开发证书的直接跳过第一步 第一步:申请"开发证书" 进入苹果开发者99美元账号: 选择:Certificates, Identifiers & Profiles 关于 ...

  4. iOS真机UI调试利器——Reveal

    做iOS的开发,UI是非常非常重要的一环.调试时我们一般用模拟器,提交前用真机做测试.用模拟器来调试UI效果虽然快捷方便,但有时仍然希望有更强大 的工具来帮助分析UI,尤其是专注在UI的效果调试时.最 ...

  5. ios 利用Reveal来调试界面1 --模拟器(步骤详解)

    Reveal是一个程序界面调试工具,可以调试iOS apps和tvOS apps.使用Reveal,我们可以在开发时动态地查看和修改应用程序的界面.避免每次修改UI的时候都要重新运行程序.接下来按照规 ...

  6. iOS开发笔记 - 界面调试神器Reveal

    http://blog.csdn.net/jackfrued/article/details/50934092     Reveal是iOS开发工具中的神器之一,它能够在应用程序运行过程中调试应用程序 ...

  7. Delphi XE4 For IOS中程序的调试(虚拟机,真实机和win32)

    安装完之后,大家可以看一下XE4可以新建的工程类型: File->New: 是不是多出了FireMonkey Mobile Application这一个选项呀! 然后你再点击这个菜单项,弹出Fi ...

  8. iOS 12 真机调试 Xcode 9 提示 Could not locate device support files.

    升级 iOS 12 之后,使用 Xcode 9 真机调试会提示错误: Could not locate device support files. This iPhone 6 Plus is runn ...

  9. iOS添加测试设备与调试

    转至:http://www.lidaze.com/   在上一篇博客中,已经购买好了开发账号,既然都交了钱了,就做点有意义的事吧!要想使用真机测试,需要准备如下: 1.证书:安装到电脑上的cer文件, ...

随机推荐

  1. Mybatis学习笔记之---CRUD(增删改查)

    Mybatis的CRUD(增删改查) 1.pom.xml <dependencies> <dependency> <groupId>junit</groupI ...

  2. jenkins 管理员密码重置

    jenkins管理员 admin的密码忘记怎么重置呢? 修改admin的加密密码为123456的加密密码 #jbcrypt:$2a$10$MiIVR0rr/UhQBqT.bBq0QehTiQVqgNp ...

  3. 从Java中的length和length()开始

    1.在没有IDE自动补齐的情况下,怎样得到数组的长度?怎样得到字符串的长度? int[] arr = new int[3]; System.out.println(arr.length);//leng ...

  4. HDU6025 Coprime Sequence(gcd)

    HDU6025 Coprime Sequence 处理出数列的 \(gcd\) 前缀和后缀,删除一个数后的 \(gcd\) 为其前缀和后缀的 \(gcd\) . 遍历数列取 \(max\) 即为答案. ...

  5. C++引用与传参

    # include <iostream> using namespace std; void Swap(int *pa, int *pb) { int t = *pa; *pa = *pb ...

  6. 2017-03-04 idea破解

    https://blog.csdn.net/qq_27686779/article/details/78870816 防止原址被删除,备份下,亲测可用 http://idea.java.sx/ 简单快 ...

  7. springboot日期格式转换

    post: @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") GET: @Dat ...

  8. 阶段2 JavaWeb+黑马旅游网_15-Maven基础_第2节 maven的安装和仓库种类_05仓库的种类和彼此关系

    maven工程里面放的是jar包的坐标. 启动项目的时候会根据jar包的坐标到仓库中找对应的坐标 maven的安装目录.conf/settings.xml文件 ${user.home}表示系统盘,用户 ...

  9. sql语句exists 用法

    参考:SQL语句exists用法,Sql语句中IN和exists的区别及应用 现在有三张表 学生表S: sno ,sname 课程表C:cno ,cname 学生选课表SC: sno ,cno 查询选 ...

  10. 在GAE中用Python编写webapp进行Post数据采集

    #!/usr/bin/env python # -*- coding: cp936 -*- # # Copyright 2007 Google Inc. # # Licensed under the ...