iOS - 断言处理与调试
一、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)
- NSAssert / NSCAssert
- 这么做的意义在于两点:
- 第一个是苹果对于断言处理在 API 层面进行了区分:
NSAssert与NSCAssert用来处理一般情况的断言NSParameterAssert与NSCParameterAssert用来处理参数化的断言
- 第二个是区别是在 Objective - C 和 C 之间进行了区分这样才有了:
NSAssert与NSCAssertNSParameterAssert与NSCParameterAssert
- 第一个是苹果对于断言处理在 API 层面进行了区分:
二、使用 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替换NSAssert,NSCParameterAssert来替换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 - 断言处理与调试的更多相关文章
- 【ionic】Mac IOS下真机调试
模拟调试不能保证真机一定没问题,所以真机调试是非常必要的一步 IOS设备 启用设备调试 在IOS设备中(Iphone,Ipad)中开始web检测器 设备->safari->高级->w ...
- iOS中web app调试(mac)
原文 iOS中web app调试(mac).md 目录 一.真机联调配置 二.mac上Safari配置及真机联调 三.iOS模拟器使用 四.在iOS模拟器中安装app 近期公司vue项目开发,目的是一 ...
- ios申请真机调试( xcode 5)详细解析
已经有开发证书的直接跳过第一步 第一步:申请"开发证书" 进入苹果开发者99美元账号: 选择:Certificates, Identifiers & Profiles 关于 ...
- iOS真机UI调试利器——Reveal
做iOS的开发,UI是非常非常重要的一环.调试时我们一般用模拟器,提交前用真机做测试.用模拟器来调试UI效果虽然快捷方便,但有时仍然希望有更强大 的工具来帮助分析UI,尤其是专注在UI的效果调试时.最 ...
- ios 利用Reveal来调试界面1 --模拟器(步骤详解)
Reveal是一个程序界面调试工具,可以调试iOS apps和tvOS apps.使用Reveal,我们可以在开发时动态地查看和修改应用程序的界面.避免每次修改UI的时候都要重新运行程序.接下来按照规 ...
- iOS开发笔记 - 界面调试神器Reveal
http://blog.csdn.net/jackfrued/article/details/50934092 Reveal是iOS开发工具中的神器之一,它能够在应用程序运行过程中调试应用程序 ...
- Delphi XE4 For IOS中程序的调试(虚拟机,真实机和win32)
安装完之后,大家可以看一下XE4可以新建的工程类型: File->New: 是不是多出了FireMonkey Mobile Application这一个选项呀! 然后你再点击这个菜单项,弹出Fi ...
- 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 ...
- iOS添加测试设备与调试
转至:http://www.lidaze.com/ 在上一篇博客中,已经购买好了开发账号,既然都交了钱了,就做点有意义的事吧!要想使用真机测试,需要准备如下: 1.证书:安装到电脑上的cer文件, ...
随机推荐
- nginx typecho config
## # You should look at the following URL's in order to grasp a solid understanding # of Nginx confi ...
- Android如何安装系统应用,及自己增加安装系统应用的接口
根据SIM卡安装系统应用 功能: 1:如何安装系统应用,apk放在system/app系统分区下面. 2:根据SIM卡的归属国家选择性的安装应用. 一:本人使用方法: 在开机的服务里面添加接口(Pac ...
- 时间戳Unix和时间之间的转换
时间戳 ---> 时间 var a="1523258178"; var start = new Date(a).format("yyyy-MM-dd hh:mm ...
- eclipse导出java项目jar包(依赖第三方jar包)
一.在项目根目录下建一个文件:MANIFEST.MF 内容: Manifest-Version: 1.0 Class-Path: lib/commons-compress-1.9.jar lib/co ...
- 不能访问windows installer服务
xp系统安装msi类型的安装程序出现以下错误: 不能访问windows installer服务. 解决办法 1:运行cmd -> regsvr32 msi.dll 运行services.msc- ...
- C#调用windows API实现 smallpdf客户端程序进行批量压缩
一.背景 Smallpdf 网站针对PDF文件提供了非常齐全的功能:PDF 与 Word.PPT.Excel.JPG 的相互转化.PDF 的压缩.编辑.合并.分割.解密.加密等功能,用户无需注册即可免 ...
- 如何将post请求转换成put和delete请求
<form:form action="${pageContext.request.contextPath}/emp" method="POST" mode ...
- mysql执行拉链表操作
拉链表需求: 1.数据量比较大 2.变化的比例和频率比较小,例如客户的住址信息,联系方式等,比如有1千万的用户数据,每天全量存储会存储很多不变的信息,对存储也是浪费,因此可以使用拉链表的算法来节省存储 ...
- apicloud 运费计算js+页面
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name ...
- 【WPF异常】在使用 ItemsSource 之前,项集合必须为空
<DataGrid x:Name=" AutoGenerateColumns="False" GridLinesVisibility="None" ...