assert大家都不陌生,在开发阶段用断言函数可以很好的进行调试。
一般OC中,我们使用的是NSAssert
。在Xcode4.2以后,Xcode会在release环境下自动把NSAssert
忽略掉,确保release环境不会出错。
assert的坑
最近遇到一个问题,线上Crash定位在了一个pod库中的assert
方法,在OC里面调用assert是用的C语言的assert。结果在release
环境下也生效了,直接造成了Crash。
查找了一些资料,找到了解决方案。
通过查询源码发现,在C语言中是如何忽略的assert
的
源码地址详见:https://opensource.apple.com/source/Libc/Libc-583/include/assert.h.auto.html
NDEBUG
是C语言中的一个标准宏,其语义适用于C89,C99,C ++ 98,C ++ 2003,C ++ 2011,C ++ 2014标准。
详见:https://stackoverflow.com/questions/2290509/debug-vs-ndebug
根据源码可知,如果定义了NDEBUG
,那么assert
就返回void
,就不会再生效。
那么我们只需要在主工程中,选择Bulid Settings
选择Preprocessor Marcos
在Release
中添加NDEBUG=1
即可。
但是需要注意的是,只修改主工程并不会对pod库生效,所以还需要对pod库进行处理!
在pod库中一个一个修改太麻烦,并且每次pod install
后就会失效,我们需要从podfile
文件入手。
post_install do |installer| |
判断是否是Release
环境,如果是就在build_settings
中的GCC_PREPROCESSOR_DEFINITIONS
添加$(inherited)
和 NDEBUG=1
。
$(inherited)
是继承Pods
工程的设置
添加完后执行pod install
,就可以发现每个pod库的Preprocessor Marcos
中,在Release
下都添加了NDEBUG=1
NSAssert介绍
说完assert,再来说说我们常用的NSAssert
NSAssert
细分为
NSAssert/NSCAsseet
NSParameterAssert / NSCParameterAssert
他们的区别如下:
NSAssert 和 NSParameterAssert 只适用于OC环境中。NSCAsseet 和 NSCParameterAssert 适用于C语言环境中
当 NSAssert 或 NSParameterAssert 的条件不满足时,断言处理器会调用
-handleFailureInMethod:object:file:lineNumber:description:
方法。
当 NSCAssert 或 NSCParameterAssert 的条件不满足时,断言处理器会调用-handleFailureInFunction:file:lineNumber:description:
方法。
NSAssert和NSAssert都有一些变体,例如NSAssert1
、NSAssert2
、NSCAssert1
、NSCAssert2
等等。他们的区别是会输出不同的参数。
具体信息详见官方文档:https://developer.apple.com/documentation/foundation/nsassert1
从Xcode4.2以后,会自动在release环境下忽略NSAssert
等断言函数。它是通过定义 NS_BLOCK_ASSERTIONS
宏实现的,确保不会对release环境有影响。
我们可以在很多第三方库中看到他们定义的assert,关闭条件也是基于NS_BLOCK_ASSERTIONS
#ifndef _GTMDevAssert |
NSAssert
和NSCAssert
底层都是通过NSAssertionHandler
来实现的,只有2个实现方法
-handleFailureInMethod:object:file:lineNumber:description:
NSAssert/NAParamterAssert调用-handleFailureInFunction:file:lineNumber:description:
NSCAssert/NACParamterAssert调用
Xcode的开关在
我们也可以自定义NSAssert
继承NSAssertionHandler,重写handleFailureInFunction:file:lineNumber:description
和handleFailureInMethod:object:file:lineNumber:description:
在AppDelegate中注册
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { |
NSAssert的坑
NSAssert在OC中,会存在循环引用问题
- (IBAction)buttonAction:(UIButton *)sender { |
循环引用的根源在于NSAssert定义中使用了self
#if !defined(_NSAssertBody) |
我们可以试用@weakself 和 @strongself来解决。不过在release环境下,assert并不会调用。所以推荐试用NSCAssert
和 NSParameterAssert