我在基本了解了 Swift 中 assert 方法的使用之后,冒出来的问题是: 为什么要用 @autoclosure
而不是一个直接的布尔类型.
第一次看到 @autoclosure 是在 assert
方法中看到的.
它要达到的目的是类似 C 语言中的 assert 宏.
那首先来看 C 语言中 assert 宏是什么样的
C 语言中的assert
定义在标准库中的assert.h
头文件中
我先用伪代码来表示一下:
#如果是非调试模式
#定义 assert(e) 不做事情
#否则即是调试模式
#定义 assert(e) 如果 表达式 e 求值结果 为true 的话,不做任何事情 ,否则 报告错误(错误信息,包含当前源文件,行号,方法名,参数)
实际代码可以参考 : bionic的实现 assert.h
#ifdef NDEBUG
#define assert(e) ((void)0)
#else
#define assert(e) ((e) ? (void)0 : __assert2(__FILE__, __LINE__,__func__,#e))
#endif
由于C语言有预编译的特性. 使用了assert调用.如果是使用 RELEASE 模式编译的话,
assert的调用都变成了 ((void)0)
都变成了空.
调试模式下,当结果不为真时,打印一下错误信息.
但是Swift这样的现代语言没有预编译,和宏展开.
在Swift中遇到这到一个问题.原话如下:
When implementing assert() in Swift, the first challenge we encounter is that there is no obvious way for a function to accept an expression without evaluating it. For example, say we tried to use
例如下面的使用场景:
func fileAtPath(path:String)->Bool{
print("\(__FUNCTION__)")
return NSFileManager.defaultManager().fileExistsAtPath(path)
}
assert(fileAtPath("/tmp/nofile"))
上面的使用场景中,如果是C语言的assert调用的话,在RELEASE模式下,assert语句整个都被擦除了.
但是在Swift中已经没有这样的宏展开. 所以上面assert
方法还会被调用.
那如果Swift中的 assert
的实现是如下的话:
func assert(boolValue:Bool){
#if !NDEBUG // 如果是调试模式
if !boolValue{
// 报告错误
abort() // 终止程序执行
}
#endif
}
关键的时,fileAtPath("/tmp/nofile")
会先被调用. 但是这个fileAtPath
的参数表达式的执行在 RELEASE模式下是没有意义的了.
因为assert在RELEASE模式下相当于一个空函数.
总结: 如果不经验特殊的处理. Swift中的 assert
调用,在 RELEASE模式下也会导致,参数表达式执行开销.
于是Swift,尝试将实现改为接受一个闭包而不是 Bool类型的参数.
如下:
func assert(predicate:() -> Bool){
#if !NDEBUG // 如果是调试模式
if !predicate(){
// 报告错误
abort() // 终止程序执行
}
#endif
}
接收一个闭包的话,在RELEASE模式下,predicate闭包不会执行,也即其中表达式不会被执行. 避免了不必要的语句执行开销.
但是这有一个问题就是, assert
的使用将变得不方便了.
如果像上面声明的话,我们需要像下面这样写调用assert
方法
assert({ fileAtPath("/tmp/nofile") })
写起来复杂,理解起来也复杂,反正是增加了复杂性.及使用难度.
Swift 通过 引入了编译属性 autoclosure
来解决这个问题.
顾名思义的话,即自动闭包. 自动帮我们生成闭包?
这样assert
函数的实现改为如下:
func assert(predicate:@autoclosure () -> Bool){
#if !NDEBUG // 如果是调试模式
if !predicate(){
// 报告错误
abort() // 终止程序执行
}
#endif
}
调用简化如下:
assert(fileAtPath("/tmp/nofile"))
于是问题得到解决, 好写,并且,效率不受影响.
func &&(lhs: BooleanType, rhs: @autoclosure () -> BooleanType) -> Bool{
return lhs.boolValue ? rhs().boolValue: false
}
逻辑或的特点是: 如果前面的一个表达式求值为true的话,后面的表达式就不用求值了. 于是实现大概是如下代码:
func ||(lhs:BooleanType, rhs: @autoclosure () -> BooleanType) -> Bool{
return lhs.boolValue ? true: rhs().boolValue()
}
a ?? b
的执行效果相当于 a != nil ? a! : b
这个语法糖的声明如下:func ??<T>(optional: T?, defaultValue: @autoclosure () -> T) -> T
func ??<T>(optional: T?, defaultValue: @autoclosure () -> T?) -> T?
于是上面的方法的实现细节可能如下, from http://swifter.tips/autoclosure/
func ??<T>(optional : T?, defaultValue: @autoclosure () -> T) -> T{
switch optional{
case .Some(let value):
return value
case .None:
return defaultValue()
}
}
因为 : 对应方法的调用都,可能不知道 @autoclosure对应的表达式,可能不会被调用. 因为它看起来就是一个会被要执行的表达式.
Swift中对 @autoclosure的使用主要是如下的函数
func precondition(@autoclosure condition: () -> Bool, @autoclosure _ message: () -> String = default, file: StaticString = default, line: UWord = default)
@noreturn func preconditionFailure(@autoclosure message: () -> String = default, file: StaticString = default, line: UWord = default)
@noreturn func fatalError(@autoclosure message: () -> String = default, file: StaticString = default, line: UWord = default)
func assertionFailure(@autoclosure message: () -> String = default, file: StaticString = default, line: UWord = default)
func assert(@autoclosure condition: () -> Bool, @autoclosure _ message: () -> String = default, file: StaticString = default, line: UWord = default)
func &&<T : BooleanType>(lhs: T, rhs: @autoclosure () -> Bool) -> Bool
func ||<T : BooleanType, U : BooleanType>(lhs: T, @autoclosure rhs: () -> U) -> Bool
func ??<T>(optional: T?, @autoclosure defaultValue: () -> T?) -> T?
func ??<T>(optional: T?, @autoclosure defaultValue: () -> T) -> T
上面的几个方法: assert类,上面讨论得比较多了,
值得注意的是: precondition
类, 如果不满足其条件的话,在 RELEASE模式下也会导致应用停止运行.
一个简单的使用示例 :
func checkTrue(@autoclosure condition:() -> Bool, @autoclosure _ message:() -> String = "assertion failed"){
if !condition(){
print(message())
}
}
checkTrue(true)
1
lizhuoli 2015-06-16 10:32:47 +08:00
这个写的不错,Swift的Assert本身确实不是非常方便。@autoclosure还没有细看,感谢了。
|
2
mailworks 2015-12-20 16:26:12 +08:00
赞。
|
3
ChouJum 2017-12-07 09:48:21 +08:00
感谢,@autoclosure 的存在解疑答惑了
|