Swift Param Attribute - @autoclosure why? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
iOS 开发实用技术导航
NSHipster 中文版
http://nshipster.cn/
cocos2d 开源 2D 游戏引擎
http://www.cocos2d-iphone.org/
CocoaPods
http://cocoapods.org/
Google Analytics for Mobile 统计解决方案
http://code.google.com/mobile/analytics/
WWDC
https://developer.apple.com/wwdc/
Design Guides and Resources
https://developer.apple.com/design/
Transcripts of WWDC sessions
http://asciiwwdc.com
Cocoa with Love
http://cocoawithlove.com/
Cocoa Dev Central
http://cocoadevcentral.com/
NSHipster
http://nshipster.com/
Style Guides
Google Objective-C Style Guide
NYTimes Objective-C Style Guide
Useful Tools and Services
Charles Web Debugging Proxy
Smore
banxi1988
V2EX    iDev

Swift Param Attribute - @autoclosure why?

  •  
  •   banxi1988
    banxi1988 2015-06-10 18:10:19 +08:00 4204 次点击
    这是一个创建于 3786 天前的主题,其中的信息可能已经有所发展或是发生改变。

    我在基本了解了 Swift 中 assert 方法的使用之后,冒出来的问题是: 为什么要用 @autoclosure
    而不是一个直接的布尔类型.

    为什么要用 @autoclosure?

    第一次看到 @autoclosure 是在 assert 方法中看到的.
    它要达到的目的是类似 C 语言中的 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中参数惰性求值问题

    在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模式下也会导致,参数表达式执行开销.

    @autoclosure 属性的引入

    于是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")) 

    于是问题得到解决, 好写,并且,效率不受影响.

    @autoclosure Swift 语言中的 其他使用场景

    1. 逻辑与或中的表达式短路执行 逻辑与的特点是: 如果前面一个表达式求值为false的话,后面的表达式就不用求值了. 于是实现可能是如下代码:
    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() } 
    1. 在 ?? 操作符 (Nil Coalescing Operator) 中的使用 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 虽好,但不要乱用

    因为 : 对应方法的调用都,可能不知道 @autoclosure对应的表达式,可能不会被调用. 因为它看起来就是一个会被要执行的表达式.

    Swift 标准库中的 @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. Building assert() in Swift, Part 1: Lazy Evaluation
    2. http://swifter.tips/autoclosure/
    3. Nil Coalescing Operator ?? 操作符
    3 条回复    2017-12-07 09:48:21 +08:00
    lizhuoli
        1
    lizhuoli  
       2015-06-16 10:32:47 +08:00
    这个写的不错,Swift的Assert本身确实不是非常方便。@autoclosure还没有细看,感谢了。
    mailworks
        2
    mailworks  
       2015-12-20 16:26:12 +08:00
    赞。
    ChouJum
        3
    ChouJum  
       2017-12-07 09:48:21 +08:00
    感谢,@autoclosure 的存在解疑答惑了
    关于     帮助文档     自助推广系统     博客     API     FAQ     Solana     5923 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 24ms UTC 02:22 PVG 10:22 LAX 19:22 JFK 22:22
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86