因爲種種緣由,掘金等第三方平臺博客再也不保證可以同步更新,歡迎移步 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