因为种种缘由,掘金等第三方平台博客再也不保证可以同步更新,欢迎移步 GitHub:github.com/kingcos/Per…。谢谢!git
Date | Notes | Swift | Xcode | Source Code |
---|---|---|---|---|
2018-04-05 | 更新并明确源代码所用版本 | 4.1 | 9.3 | Swift 4.1 Release |
2018-01-13 | 首次提交 | 4.0.3 | 9.2 | - |
您能够在 github.com/kingcos/Per… 中看到更多更新的博文,谢谢您的关注。github
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.编程
— The Swift Programming Language (Swift 4.1)swift
闭包(Closure)在 Swift 等许多语言中广泛存在。熟悉 Objective-C 的同窗必定对 Block 不陌生。二者实际上是比较相似的,相较于 Block,闭包的写法简化了许多,也十分灵活。api
在 Swift 中,@
开头一般表明着属性(Attribute)。@autoclosure
属于类型属性(Type Attribute),意味着其能够对类型(Type)做出一些限定。闭包
@autoclosure
名称中即明确了这是一种「自动」闭包,便可以让返回该参数类型的闭包做为参数;func logIfTrue(_ predicate: () -> Bool) {
if predicate() {
print(#function)
}
}
// logIfTrue(predicate: () -> Bool)
logIfTrue { 1 < 2 }
func logIfTrueWithAutoclosure(_ predicate: @autoclosure () -> Bool) {
if predicate() {
print(#function)
}
}
// logIfTrueWithAutoclosure(predicate: Bool)
logIfTrueWithAutoclosure(1 < 2)
// OUTPUT:
// logIfTrue
// logIfTrueWithAutoclosure
复制代码
@autoclosure
独有,但一般搭配使用。var array = [1, 2, 3, 4, 5]
array.removeLast()
print(array.count)
var closureVar = { array.removeLast() }
print(array.count)
closureVar()
print(array.count)
// OUTPUT:
// 4
// 4
// 3
复制代码
@escaping
,能够用于处理一些耗时操做的回调;@autoclosure
与 @escaping
是能够兼容的,放置顺序能够颠倒。func doWith(_ completion: () -> Void) {
completion()
}
func doWithAutoclosureAndEscaping(_ escaper: @autoclosure @escaping () -> Void) {
doWith {
escaper()
}
}
func doWithEscapingAndAutoclosure(_ escaper: @escaping @autoclosure () -> Void) {
doWith {
escaper()
}
}
复制代码
$SWIFT_SOURCE_CODE_PATH/test/attr/attr_autoclosure.swiftapp
inout
与 @autoclosure
不兼容,且没有实际意义;@autoclosure
不适用于函数的可变参数(Variadic Parameters)。$SWIFT_SOURCE_CODE_PATH/stdlib/public/core/编程语言
// Bool.swift
extension Bool {
@_inlineable // FIXME(sil-serialize-all)
@_transparent
@inline(__always)
public static func && (lhs: Bool, rhs: @autoclosure () throws -> Bool) rethrows
-> Bool {
return lhs ? try rhs() : false
}
@_inlineable // FIXME(sil-serialize-all)
@_transparent
@inline(__always)
public static func || (lhs: Bool, rhs: @autoclosure () throws -> Bool) rethrows
-> Bool {
return lhs ? true : try rhs()
}
}
// Optional.swift
@_inlineable // FIXME(sil-serialize-all)
@_transparent
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T)
rethrows -> T {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
@_inlineable // FIXME(sil-serialize-all)
@_transparent
public func ?? <T>(optional: T?, defaultValue: @autoclosure () throws -> T?)
rethrows -> T? {
switch optional {
case .some(let value):
return value
case .none:
return try defaultValue()
}
}
复制代码
&&
、||
以及 ??
属于短路运算符,即当表达式左边的结果已经能够决定整个运算符的返回值时(运算符的本质也是函数),右边便没有必要运算。利用了 @autoclosure
使得运算符右边能够为闭包,再凭借 Delay Evaluation 特性保证了「短路」。var flag = 0
var age: Int? = nil
func getAgeA() -> Int? {
flag += 10
return 20
}
func getAgeB() -> Int? {
flag += 100
return nil
}
age ?? getAgeA() ?? getAgeB()
print(flag)
// OUTPUT:
// 10
复制代码
@autoclosure
,一是能够直接将闭包直接做为参数;二是当 Release 模式时,Closure 没有必要执行,便可节省开销(XCTest 和 Dispatch 中的部分方法同理)。// AssertCommon.swift
@_inlineable // FIXME(sil-serialize-all)
public // COMPILER_INTRINSIC
func _undefined<T>( _ message: @autoclosure () -> String = String(),
file: StaticString = #file, line: UInt = #line
) -> T {
_assertionFailure("Fatal error", message(), file: file, line: line, flags: 0)
}
// Assert.swift
@_inlineable // FIXME(sil-serialize-all)
@_transparent
public func assert( _ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String = String(),
file: StaticString = #file, line: UInt = #line
) {
// Only assert in debug mode.
// 在 Debug 模式且条件不成立,断言失败
if _isDebugAssertConfiguration() {
if !_branchHint(condition(), expected: true) {
_assertionFailure("Assertion failed", message(), file: file, line: line,
flags: _fatalErrorFlags())
}
}
}
@_inlineable // FIXME(sil-serialize-all)
@_transparent
public func precondition( _ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String = String(),
file: StaticString = #file, line: UInt = #line
) {
// Only check in debug and release mode. In release mode just trap.
if _isDebugAssertConfiguration() {
if !_branchHint(condition(), expected: true) {
_assertionFailure("Precondition failed", message(), file: file, line: line,
flags: _fatalErrorFlags())
}
} else if _isReleaseAssertConfiguration() {
let error = !condition()
Builtin.condfail(error._value)
}
}
@_inlineable // FIXME(sil-serialize-all)
@inline(__always)
public func assertionFailure( _ message: @autoclosure () -> String = String(),
file: StaticString = #file, line: UInt = #line
) {
if _isDebugAssertConfiguration() {
_assertionFailure("Fatal error", message(), file: file, line: line,
flags: _fatalErrorFlags())
}
else if _isFastAssertConfiguration() {
_conditionallyUnreachable()
}
}
@_inlineable // FIXME(sil-serialize-all)
@_transparent
public func preconditionFailure( _ message: @autoclosure () -> String = String(),
file: StaticString = #file, line: UInt = #line
) -> Never {
// Only check in debug and release mode. In release mode just trap.
if _isDebugAssertConfiguration() {
_assertionFailure("Fatal error", message(), file: file, line: line,
flags: _fatalErrorFlags())
} else if _isReleaseAssertConfiguration() {
Builtin.int_trap()
}
_conditionallyUnreachable()
}
@_inlineable // FIXME(sil-serialize-all)
@_transparent
public func fatalError( _ message: @autoclosure () -> String = String(),
file: StaticString = #file, line: UInt = #line
) -> Never {
_assertionFailure("Fatal error", message(), file: file, line: line,
flags: _fatalErrorFlags())
}
@_inlineable // FIXME(sil-serialize-all)
@_transparent
public func _precondition( _ condition: @autoclosure () -> Bool, _ message: StaticString = StaticString(),
file: StaticString = #file, line: UInt = #line
) {
// Only check in debug and release mode. In release mode just trap.
if _isDebugAssertConfiguration() {
if !_branchHint(condition(), expected: true) {
_fatalErrorMessage("Fatal error", message, file: file, line: line,
flags: _fatalErrorFlags())
}
} else if _isReleaseAssertConfiguration() {
let error = !condition()
Builtin.condfail(error._value)
}
}
@_inlineable // FIXME(sil-serialize-all)
@_transparent
public func _debugPrecondition( _ condition: @autoclosure () -> Bool, _ message: StaticString = StaticString(),
file: StaticString = #file, line: UInt = #line
) {
// Only check in debug mode.
if _isDebugAssertConfiguration() {
if !_branchHint(condition(), expected: true) {
_fatalErrorMessage("Fatal error", message, file: file, line: line,
flags: _fatalErrorFlags())
}
}
}
@_inlineable // FIXME(sil-serialize-all)
@_transparent
public func _sanityCheck( _ condition: @autoclosure () -> Bool, _ message: StaticString = StaticString(),
file: StaticString = #file, line: UInt = #line
) {
#if INTERNAL_CHECKS_ENABLED
if !_branchHint(condition(), expected: true) {
_fatalErrorMessage("Fatal error", message, file: file, line: line,
flags: _fatalErrorFlags())
}
#endif
}
复制代码
It’s common to call functions that take autoclosures, but it’s not common to implement that kind of function.函数
NOTE优化
Overusing autoclosures can make your code hard to understand. The context and function name should make it clear that evaluation is being deferred.
— The Swift Programming Language (Swift 4.1)
@autoclosure
的函数,若是确有必要,也须要作到明确、清晰。The compiler intrinsic which is called to lookup a string in a table of static string case values.(笔者译:编译器内置,即在一个静态字符串值表中查找一个字符串。)
— $SWIFT_SOURCE_CODE_PATH/stdlib/public/core/StringSwitch.swift
In computer software, in compiler theory, an intrinsic function (or builtin function) is a function (subroutine) available for use in a given programming language which implementation is handled specially by the compiler. Typically, it may substitute a sequence of automatically generated instructions for the original function call, similar to an inline function. Unlike an inline function, the compiler has an intimate knowledge of an intrinsic function and can thus better integrate and optimize it for a given situation.(笔者译:在计算机软件领域,编译器理论中,内置函数(或称内建函数)是在给定编程语言中能够被编译器所专门处理的的函数(子程序)。一般,它能够用一系列自动生成的指令代替原来的函数调用,相似于内联函数。与内联函数不一样的是,编译器更加了解内置函数,所以能够更好地整合和优化特定状况。)。
— WikiPedia
也欢迎您关注个人微博 @萌面大道V