# 特性(Attributes)
在 Swift 中有兩種特性,分別用于修飾聲明和類型。特性提供了有關聲明和類型的更多信息。例如,使用 `discardableResult` 特性聲明的函數,表明該函數雖然有返回值,但如果沒有使用該返回值,編譯器不會產生警告。
您可以通過以下方式指定一個特性,通過符號 `@` 后跟特性的名稱和特性接收的任何參數:
@`特性名`
@`特性名`(`特性參數`)
有些聲明特性通過接收參數來指定特性的更多信息以及它是如何修飾某個特定的聲明的。這些_特性的參數_寫在圓括號內,它們的格式由它們所屬的特性來定義。
## 聲明特性 {#declaration-attributes}
聲明特性只能應用于聲明。
### `available` {#available}
將 `available` 特性用于聲明時,表示該聲明的生命周期是相對于特定的平臺和操作系統版本。
`available` 特性經常與參數列表一同出現,該參數列表至少有兩個特性參數,參數之間由逗號分隔。這些參數由以下這些平臺名字中的一個起頭:
- `iOS`
- `iOSApplicationExtension`
- `macOS`
- `macOSApplicationExtension`
- `watchOS`
- `watchOSApplicationExtension`
- `tvOS`
- `tvOSApplicationExtension`
- `swift`
當然,你也可以用一個星號(`*`)來表示上面提到的所有平臺。指定 Swift 版本的 `available` 特性參數,不能使用星號表示。
其余的參數,可以按照任何順序出現,并且可以添加關于聲明生命周期的附加信息,包括重要事件。
- `unavailable` 參數表示該聲明在指定的平臺上是無效的。當指定 Swift 版本可用性時不可使用該參數。
- `introduced` 參數表示指定平臺從哪一版本開始引入該聲明。格式如下:
`introduced`: `版本號`
*版本號*由一至三個正整數構成,由句點分隔的。
- `deprecated` 參數表示指定平臺從哪一版本開始棄用該聲明。格式如下:
`deprecated`: `版本號`
可選的*版本號*由一個或多個正整數構成,由句點分隔的。省略版本號表示該聲明目前已棄用,而不提供有關棄用發生時間的任何信息。如果你省略了版本號,冒號(`:`)也可省略。
- `obsoleted` 參數表示指定平臺或語言從哪一版本開始廢棄該聲明。當一個聲明被廢棄后,它就從平臺或語言中移除,不能再被使用。格式如下:
`obsoleted`: `版本號`
*版本號*由一至三個正整數構成,由句點分隔的。
- `message` 參數用來提供文本信息。當使用被棄用或者被廢棄的聲明時,編譯器會拋出該信息作為警告或錯誤。格式如下:
`message`: `信息內容`
_信息內容_由一個字符串構成。
- `renamed` 參數用來提供文本信息,用以表示被重命名聲明的新名字。當使用聲明的舊名字時,編譯器會報錯并提示新名字。格式如下:
`renamed`: `新名字`
_新名字_由一個字符串構成。
你可以將帶有 `renamed` 和 `unavailable` 參數的 `available` 特性應用于類型別名聲明,如下所示,來表明框架和庫發行版本之間的聲明名稱已經被更改。這個組合會導致聲明已重命名的編譯時錯誤。
```swift
// 首發版本
protocol MyProtocol {
// 這里是協議定義
}
```
```swift
// 后續版本重命名了 MyProtocol
protocol MyRenamedProtocol {
// 這里是協議定義
}
@available(*, unavailable, renamed:"MyRenamedProtocol")
typealias MyProtocol = MyRenamedProtocol
```
你可以在某個聲明上使用多個 `available` 特性,以指定該聲明在不同平臺和 Swift 版本上的可用性。如果當前目標與 `available` 特性中指定的平臺或語言版本不匹配時,該聲明將會被忽略。如果你使用了多個 `available` 特性,則最終效果是平臺和 Swift 可用性的組合。
如果 `available` 特性除了平臺名稱或者語言版本參數之外,只指定了一個 `introduced` 參數,那么可以使用以下簡寫語法代替:
@available(`平臺名稱` `版本號`, *)
@available(swift `版本號`)
`available` 特性的簡寫語法簡潔的表示了多個平臺的可用性。盡管這兩種形式在功能上是相同的,但請盡可能地使用簡寫語法形式。
```swift
@available(iOS 10.0, macOS 10.12, *)
class MyClass {
// 這里是類定義
}
```
當需要同時指定 Swift 版本和平臺可用性,需要使用一個單獨的 `available` 特性來指明 Swift 版本,以及一個或者多個 `available` 特性來聲明平臺可用性。
```swift
@available(swift 3.0.2)
@available(macOS 10.12, *)
struct MyStruct {
// 這里是結構體定義
}
```
### `discardableResult` {#discardableresult}
該特性用于的函數或方法聲明,以抑制編譯器中函數或方法被調用而其返回值沒有被使用時的警告。
### `dynamicCallable` {#dynamiccallable}
該特性用于類、結構體、枚舉或協議,以將該類型的實例視為可調用的函數。該類型必須實現 `dynamicallyCall(withArguments:)`、`dynamicallyCall(withKeywordArguments:)` 方法之一,或兩者同時實現。
你可以調用 `dynamicCallable` 特性的實例,就像是調用一個任意數量參數的函數。
```swift
@dynamicCallable
struct TelephoneExchange {
func dynamicallyCall(withArguments phoneNumber: [Int]) {
if phoneNumber == [4, 1, 1] {
print("Get Swift help on forums.swift.org")
} else {
print("Unrecognized number")
}
}
}
let dial = TelephoneExchange()
// 使用動態方法調用
dial(4, 1, 1)
// 打印“Get Swift help on forums.swift.org”
dial(8, 6, 7, 5, 3, 0, 9)
// 打印“Unrecognized number”
// 直接調用底層方法
dial.dynamicallyCall(withArguments: [4, 1, 1])
```
`dynamicallyCall(withArguments:)` 方法的聲明必須至少有一個參數遵循 [`ExpressibleByArrayLiteral`](https://developer.apple.com/documentation/swift/expressiblebyarrayliteral) 協議,如 `[Int]`,而返回值類型可以是任意類型。
如果實現 `dynamicallyCall(withKeywordArguments:)` 方法,則可以在動態方法調用中包含標簽。
```swift
@dynamicCallable
struct Repeater {
func dynamicallyCall(withKeywordArguments pairs: KeyValuePairs<String, Int>) -> String {
return pairs
.map { label, count in
repeatElement(label, count: count).joined(separator: " ")
}
.joined(separator: "\n")
}
}
let repeatLabels = Repeater()
print(repeatLabels(a: 1, b: 2, c: 3, b: 2, a: 1))
// a
// b b
// c c c
// b b
// a
```
`dynamicallyCall(withKeywordArguments:)` 方法聲明必須只有一個遵循 [`ExpressibleByDictionaryLiteral`](https://developer.apple.com/documentation/swift/expressiblebydictionaryliteral) 協議的參數,返回值可以任意類型。參數的 [`Key`](https://developer.apple.com/documentation/swift/expressiblebydictionaryliteral/2294108-key) 必須遵循 [`ExpressibleByStringLiteral`](https://developer.apple.com/documentation/swift/expressiblebystringliteral) 協議。上述的示例使用 [`KeyValuePairs`](https://developer.apple.com/documentation/swift/keyvaluepairs) 作為參數類型,以便調用者可以傳入重復的參數標簽,`a` 和 `b` 在調用 `repeat`中多次使用。
如果你同時實現兩種 `dynamicallyCall` 方法,則當在方法調用中包含關鍵字參數時,會調用 `dynamicallyCall(withKeywordArguments:)` 方法,否則調用 `dynamicallyCall(withArguments:)` 方法。
你只能調用參數和返回值與 `dynamicallyCall` 方法實現匹配的動態調用實例。在下面示例的調用無法編譯,因為其 `dynamicallyCall(withArguments:)` 實現不接受 `KeyValuePairs<String, String>` 參數。
```swift
repeatLabels(a: "four") // Error
```
### `dynamicMemberLookup` {#dynamicmemberlookup}
該特性用于類、結構體、枚舉或協議,讓其能在運行時查找成員。該類型必須實現 `subscript(dynamicMemberLookup:)` 下標。
在顯式成員表達式中,如果指定成員沒有相應的聲明,則該表達式被理解為對該類型的 `subscript(dynamicMemberLookup:)` 下標調用,將有關該成員的信息作為參數傳遞。下標接收參數既可以是鍵路徑,也可以是成員名稱字符串;如果你同時實現這兩種方式的下標調用,那么以鍵路徑參數方式為準。
`subscript(dynamicMemberLookup:)` 實現允許接收 [`KeyPath`](https://developer.apple.com/documentation/swift/keypath),[`WritableKeyPath`](https://developer.apple.com/documentation/swift/writablekeypath) 或 [`ReferenceWritableKeyPath`](https://developer.apple.com/documentation/swift/referencewritablekeypath) 類型的鍵路徑參數。它可以使用遵循 [`ExpressibleByStringLiteral`](https://developer.apple.com/documentation/swift/expressiblebystringliteral) 協議的類型作為參數來接受成員名 -- 通常情況下是 `String`。下標返回值類型可以為任意類型。
按成員名進行的動態成員查找可用于圍繞編譯時無法進行類型檢查的數據創建包裝類型,例如在將其他語言的數據橋接到 `Swift` 時。例如:
```swift
@dynamicMemberLookup
struct DynamicStruct {
let dictionary = ["someDynamicMember": 325,
"someOtherMember": 787]
subscript(dynamicMember member: String) -> Int {
return dictionary[member] ?? 1054
}
}
let s = DynamicStruct()
// 使用動態成員查找
let dynamic = s.someDynamicMember
print(dynamic)
// 打印“325”
// 直接調用底層下標
let equivalent = s[dynamicMember: "someDynamicMember"]
print(dynamic == equivalent)
// 打印“true”
```
根據鍵路徑來動態地查找成員,可用于創建一個包裹數據的包裝類型,該類型支持在編譯時期進行類型檢查。例如:
```swift
struct Point { var x, y: Int }
@dynamicMemberLookup
struct PassthroughWrapper<Value> {
var value: Value
subscript<T>(dynamicMember member: KeyPath<Value, T>) -> T {
get { return value[keyPath: member] }
}
}
let point = Point(x: 381, y: 431)
let wrapper = PassthroughWrapper(value: point)
print(wrapper.x)
```
### `frozen` {#frozen}
針對枚舉或者結構體的聲明使用該特性,可以限制你對該類型的修改。它只有在編譯迭代庫時被允許使用。未來版本的庫不能通過添加、刪除或重新排序枚舉的 case 或結構的存儲實例屬性來更改聲明。在未凍結的類型上,這些操作都是允許的,但是他們破壞了凍結類型的 ABI 兼容性。
> 注意
> 當編譯器不處于迭代庫的模式,所有的結構體和枚舉都是隱性凍結,并且該特性會被忽視。
在迭代庫的模式中,與未凍結結構體和枚舉的成員進行交互的代碼在被編譯時,允許它在不重新編譯的情況下繼續工作,即使在新版本的庫中添加、刪除或重新排序該類型的成員。編譯器用類似運行時查找信息和添加間接層的技術使之可能。將一個枚舉或者結構體標記為凍結將以放棄這種靈活性為代價來獲取性能上的提升:未來版本的庫只能對類型進行有限的更改,但編譯器可以對與類型成員交互的代碼進行額外的優化。
使用凍結類型,結構體存儲屬性的類型以及枚舉 case 的關聯值必須是 `public` 或使用 `usablefrominline` 特性標記。凍結結構體的屬性不能有屬性觀察器,為存儲實例屬性提供初始值的表達式必須遵循與 `inlinable` 函數相同的限制,如 [`inlinable`](#inlinable) 中所述。
要在命令行上啟用迭代庫模式,請將 `-enable-library-evolution` 選項傳遞給 Swift 編譯器。要在 Xcode 中支持它,則將生成設置 “Build Libraries for Distribution”(`BUILD_LIBRARY_FOR_DISTRIBUTION`)設置為 Yes,詳情查看 [`Xcode 幫助文檔`](https://help.apple.com/xcode/mac/current/#/dev04b3a04ba)。
針對凍結枚舉的 switch 語法,不需要 `default` case,就像 [`對未來枚舉的 case 進行 switch`](./05_Statements.md#future-case)。在針對凍結枚舉使用 switch 語法時包含 `default` 或 `@unknown default` case 將生成警告,因為該代碼永遠不會執行。
### `GKInspectable` {#gkinspectable}
應用此特性可將自定義 GameplayKit 組件屬性公開到 SpriteKit 編輯器 UI。應用此特性同時表示應用了 `objc` 特性。
### `inlinable` {#inlinable}
該特性用于函數、方法、計算屬性、下標、便利構造器或析構器的聲明,以將該聲明的實現公開為模塊公開接口的一部分。編譯器允許在調用處把 `inlinable` 標記的符號替換為符號實現的副本。
內聯代碼可以與任意模塊中 `public` 訪問級別的符號進行交互,同時可以與在相同模塊中標記 `usableFromInline` 特性的 `internal` 訪問級別的符號進行交互。內聯代碼不能與 `private` 或 `fileprivate` 級別的符號進行交互。
該特性不能用于嵌套在函數內的聲明,也不能用于 `fileprivate` 或 `private` 訪問級別的聲明。在內聯函數內定義的函數和閉包都是隱式內聯的,即使他們不能標記該特性。
### `nonobjc` {#nonobjc}
針對方法、屬性、下標、或構造器的聲明使用該特性將覆蓋隱式的 `objc` 特性。`nonobjc` 特性告訴編譯器該聲明不能在 Objective-C 代碼中使用,即便它能在 Objective-C 中表示。
該特性用在擴展中,與在沒有明確標記為 `objc` 特性的擴展中給每個成員添加該特性具有相同效果。
可以使用 `nonobjc` 特性解決標有 `objc` 的類中橋接方法的循環問題,該特性還允許對標有 `objc` 的類中的構造器和方法進行重載。
標有 `nonobjc` 特性的方法不能重寫標有 `objc` 特性的方法。然而,標有 `objc` 特性的方法可以重寫標有 `nonobjc` 特性的方法。同樣,標有 `nonobjc` 特性的方法不能滿足標有 `@objc` 特性的協議中的方法要求。
### `NSApplicationMain` {#nsapplicationmain}
在類上使用該特性表示該類是應用程序委托類。使用該特性與調用 `NSApplicationMain(_:_:)` 函數的效果相同。
如果你不想使用這個特性,可以提供一個 `main.swift` 文件,并在代碼頂層調用 `NSApplicationMain(_:_:)` 函數,如下所示:
```swift
import AppKit
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
```
### `NSCopying` {#nscopying}
該特性用于修飾一個類的存儲型變量屬性。該特性將使屬性的設值方法使用傳入值的`副本`進行賦值,這個值由傳入值的 `copyWithZone(_:)` 方法返回,而不是傳入值本身。該屬性的類型必需符合 `NSCopying` 協議。
`NSCopying` 特性的行為與 Objective-C 中的 `copy` 屬性特性相似。
### `NSManaged` {#nsmanaged}
該特性用于修飾 `NSManagedObject` 子類中的實例方法或存儲型變量屬性,表明它們的實現由 `Core Data` 在運行時基于相關實體描述動態提供。對于標記了 `NSManaged` 特性的屬性,`Core Data` 也會在運行時為其提供存儲。應用這個特性也意味著 `objc` 特性。
### `objc` {#objc}
該特性用于修飾任何可以在 Objective-C 中表示的聲明。比如,非嵌套類、協議、非泛型枚舉(僅限原始值為整型的枚舉)、類的屬性和方法(包括存取方法)、協議以及協議中的可選成員、構造器以及下標運算符。`objc` 特性告訴編譯器這個聲明可以在 Objective-C 代碼中使用。
該特性用在擴展中,與在沒有明確標記為 `nonobjc` 特性的擴展中給每個成員添加該特性具有相同效果。
編譯器隱式地將 `objc` 特性添加到 Objective-C 中定義的任何類的子類。但是,子類不能是泛型的,并且不能繼承于任何泛型類。你可以將 `objc` 特性顯式添加到滿足這些條件的子類,來指定其 Objective-C 名稱,如下所述。添加了 `objc` 的協議不能繼承于沒有此特性的協議。
在以下情況中同樣會隱式的添加 `objc` 特性:
- 父類有 `objc` 特性,且重寫為子類的聲明。
- 遵循帶有 `objc` 特性協議的聲明。
- 帶有 `IBAction`、`IBSegueAction`、`IBOutlet`、`IBDesignable`、`IBInspectable`、`NSManaged` 或 `GKInspectable` 特性的聲明。
如果你將 `objc` 特性應用于枚舉,每一個枚舉 case 都會以枚舉名稱和 case 名稱組合的方式暴露在 Objective-C 代碼中。實例名稱的首字母大寫。例如,在 Swift 枚舉類型 `Planet` 中有一個名為 `Venus` 的 case,該 case 暴露在 Objective-C 代碼中時叫做 `PlanetVenus`。
`objc` 特性可以接受一個可選的特性參數,由標識符構成。當你想把 `objc` 所修飾的實體以一個不同的名字暴露給 Objective-C 時,你就可以使用這個特性參數。你可以使用這個參數來命名類、枚舉類型、枚舉 case、協議、方法、存取方法以及構造器。如果你要指定類、協議或枚舉在 Objective-C 下的名稱,請在名稱上包含三個字母的前綴,就像 [Objective-C 編程](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Introduction/Introduction.html#//apple-ref/doc/uid/TP40011210) 中的 [約定](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/ProgrammingWithObjectiveC/Conventions/Conventions.html#//apple-ref/doc/uid/TP40011210-CH10-SW1)描述的一樣。下面的例子把 `ExampleClass` 中的 `enabled` 屬性的取值方法暴露給 Objective-C,名字是 `isEnabled`,而不是它原來的屬性名。
```swift
class ExampleClass: NSObject {
@objc var enabled: Bool {
@objc(isEnabled) get {
// 返回適當的值
}
}
}
```
更多相關信息,請參考 [把 Swift 導入 Objective-C](https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis/importing_swift_into_objective-c)。
> 注意
> 具有 `objc` 特性的實參也會改變那個聲明的運行時名稱。在調用與 Objective-C 運行時交互的函數時,比如 [NSClassFromString](https://developer.apple.com/documentation/foundation/1395135-nsclassfromstring),以及在應用程序的 info.plist 文件中指定類名時,你會用到運行時名稱。如果你通過傳遞實參的方式來指定名稱,這個名稱會作為 Objective-C 代碼中的名稱和運行時名稱。如果你不使用這個實參,在 Objective-C 代碼中使用的名稱會與 Swift 代碼中的名稱匹配,并且運行時名稱會遵循標準 Swift 編譯器名稱管理的慣例。
### `objcMembers` {#objcmembers}
該特性用于類聲明,以將 `objc` 特性應用于該類、擴展、子類以及子類的擴展的所有 Objective-C 兼容成員。
大多數代碼應該使用 `objc` 特性,以暴露所需的聲明。如果需要暴露多個聲明,可以將其分組到添加 `objc` 特性的擴展中。`objcMembers` 特性為大量使用 Objective-C 運行時的內省工具的庫提供了便利。添加不必要的 `objc` 特性會增加二進制體積并影響性能。
### `propertyWrapper` {#propertywrapper}
在類、結構體或者枚舉的聲明時使用該特性,可以讓其成為一個屬性包裝器。如果將該特性應用在一個類型上,將會創建一個與該類型同名的自定義特性。將這個新的特性用于類、結構體、枚舉的屬性,則可以通過包裝器的實例封裝對該屬性的訪問。局部和全局變量不能使用屬性包裝器。
包裝器必須定義一個 `wrappedValue` 實例屬性。該屬性 _wrapped value_ 是該屬性存取方法暴露的值。大多數時候,`wrappedValue` 是一個計算屬性,但它可以是一個存儲屬性。包裝器負責定義和管理其包裝值所需的任何底層存儲。編譯器通過在包裝屬性的名稱前加下劃線(`_`)來為包裝器的實例提供同步存儲。例如,`someProperty` 的包裝器存儲為 `_someProperty`。包裝器的同步存儲具有 `private` 的訪問控制級別。
擁有屬性包裝器的屬性可以包含 `willSet` 和 `didSet` 閉包,但是不能重寫編譯器合成的 `get` 和 `set` 閉包。
Swift 為屬性包裝器的構造函數提供了兩種形式的語法糖。可以在包裝值的定義中使用賦值語法,將賦值語句右側的表達式作為值傳遞給屬性包裝器構造函數中的 `wrappedValue` 參數。同樣的,你也可以為包裝器提供一些參數,這些參數將會傳遞給包裝器的構造函數。就像下面的例子,`SomeStruct` 中,定義 `SomeWrapper` 的地方各自調用了對應的構造函數。
```swift
@propertyWrapper
struct SomeWrapper {
var wrappedValue: Int
var someValue: Double
init() {
self.wrappedValue = 100
self.someValue = 12.3
}
init(wrappedValue: Int) {
self.wrappedValue = wrappedValue
self.someValue = 45.6
}
init(wrappedValue value: Int, custom: Double) {
self.wrappedValue = value
self.someValue = custom
}
}
struct SomeStruct {
// 使用 init()
@SomeWrapper var a: Int
// 使用 init(wrappedValue:)
@SomeWrapper var b = 10
// 兩個都是使用 init(wrappedValue:custom:)
@SomeWrapper(custom: 98.7) var c = 30
@SomeWrapper(wrappedValue: 30, custom: 98.7) var d
}
```
屬性包裝器中 _projected value_ 是它可以用來暴露額外功能的第二個值。屬性包裝器的作者負責確認其映射值的含義并定義公開映射值的接口。若要通過屬性包裝器來映射值,請在包裝器的類型上定義 `projectedValue` 實例屬性。編譯器通過在包裝屬性的名稱前面加上美元符號(`$`)來合成映射值的標識符。例如,`someProperty` 的映射值是 `$someProperty`。映射值具有與原始包裝屬性相同的訪問控制級別。
```swift
@propertyWrapper
struct WrapperWithProjection {
var wrappedValue: Int
var projectedValue: SomeProjection {
return SomeProjection(wrapper: self)
}
}
struct SomeProjection {
var wrapper: WrapperWithProjection
}
struct SomeStruct {
@WrapperWithProjection var x = 123
}
let s = SomeStruct()
s.x // Int value
s.$x // SomeProjection value
s.$x.wrapper // WrapperWithProjection value
```
### `requires-stored-property-inits` {#requires-stored-property-inits}
該特性用于類聲明,以要求類中所有存儲屬性提供默認值作為其定義的一部分。對于從中繼承的任何類都推斷出 `NSManagedObject` 特性。
### `testable` {#testable}
將此特性應用于 `import` 聲明以導入該模塊,并更改其訪問控制以簡化對該模塊代碼的測試。這樣就能訪問被導入模塊中的任何標有 `internal` 訪問級別修飾符的實體,猶如它們被標記了 `public` 訪問級別修飾符。測試也可以訪問使用 `internal` 或者 `public` 訪問級別修飾符標記的類和類成員,就像它們是 `open` 訪問修飾符聲明的。被導入的模塊必須以允許測試的方式編譯。
### `UIApplicationMain` {#uiapplicationmain}
在類上使用該特性表示該類是應用程序委托類。使用該特性與調用 `UIApplicationMain` 函數并且把該類的名字作為委托類的名字傳遞給函數的效果相同。
如果你不想使用這個特性,可以提供一個 `main.swift` 文件,并在代碼頂層調用 `UIApplicationMain(_:_:_:_:)` 函數。比如,如果你的應用程序使用一個繼承于 `UIApplication` 的自定義子類作為主要類,你可以調用 `UIApplicationMain(_:_:_:_:)` 函數而不是使用該特性。
### `usableFromInline` {#usablefrominline}
該特性用于函數、方法、計算屬性、下標、構造器或析構器的聲明,以在同一模塊中允許該符號用于內聯代碼的聲明。聲明必須具有 `internal` 訪問級別修飾符。被標記為 `usableFromInline` 的結構體或類它們屬性的類型只能是被標記為 public 或者 `usableFromInline` 的類型。被標記為 `usableFromInline` 的枚舉,它 case 的真實值或者關聯類型只能是被標記為 public 或者 `usableFromInline` 的類型。
與 `public` 訪問修飾符相同的是,該特性將聲明公開為模塊公共接口的一部分。區別于 `public`,編譯器不允許在模塊外部的代碼通過名稱引用 `usableFromInline` 標記的聲明,即使導出了聲明符號也無法引用。但是,模塊外的代碼仍然可以通過運行時與聲明符號進行交互。
標記為 `inlinable` 特性的聲明,在內聯代碼中可以隱式使用。雖然 `inlinable` 或 `usableFromInline` 可以用于 `internal` 聲明,但這兩者不能同時使用。
### `warn-unqualified-access` {#warn-unqualified-access}
該特性應用于頂級函數、實例方法、類方法或靜態方法,以在沒有前置限定符(例如模塊名稱、類型名稱、實例變量或常量)的情況下使用該函數或方法時觸發警告。使用該特性可以減少在同一作用域里訪問的同名函數之間的歧義。
例如,Swift 標準庫包含 [`min(_:_:)`](https://developer.apple.com/documentation/swift/1538339-min/) 頂級函數和用于序列比較元素的 [`min()`](https://developer.apple.com/documentation/swift/sequence/1641174-min) 方法。序列方法聲明使用了 `warn_unqualified_access`,以減少在 `Sequence` 擴展中使用它們的歧義。
### Interface Builder 使用的聲明特性 {#declaration-attributes-used-by-interface-builder}
Interface Builder 特性是 Interface Builder 用來與 Xcode 同步的聲明特性。Swift 提供了以下的 Interface Builder 特性:`IBAction`,`IBSegueAction`,`IBOutlet`,`IBDesignable`,以及 `IBInspectable`。這些特性與 Objective-C 中對應的特性在概念上是相同的。
`IBOutlet` 和 `IBInspectable` 用于修飾一個類的屬性聲明。`IBAction` 和 `IBSegueAction` 特性用于修飾一個類的方法聲明,`IBDesignable` 用于修飾類的聲明。
應用 `IBAction`、`IBSegueAction`、`IBOutlet`、`IBDesignable` 或者 `IBInspectable` 特性都意味著同時應用 `objc` 特性。
## 類型特性 {#type-attributes}
類型特性只能用于修飾類型。
### `autoclosure` {#autoclosure}
這個特性通過把表達式自動封裝成無參數的閉包來延遲表達式的計算。它可以修飾類型為返回表達式結果類型的無參數函數類型的函數參數。關于如何使用 `autoclosure` 特性的例子,請參閱 [自動閉包](../02_language_guide/07_Closures.md#autoclosures) 和 [函數類型](./03_Types.md#function-type)。
### `convention` {#convention}
該特性用于修飾函數類型,它指出了函數調用的約定。
`convention` 特性總是與下面的參數之一一起出現。
- `swift` 參數用于表示一個 Swift 函數引用。這是 Swift 中函數值的標準調用約定。
- `block` 參數用于表示一個 Objective-C 兼容的塊引用。函數值會作為一個塊對象的引用,塊是一種 `id` 兼容的 Objective-C 對象,其中嵌入了調用函數。調用函數使用 C 的調用約定。
- `c` 參數用于表示一個 C 函數引用。函數值沒有上下文,不具備捕獲功能,并且使用 C 的調用約定。
除了少數例外,當函數需要任何其他調用約定時,可以轉換成任意調用約定的函數。非范型全局函數、不捕獲任何局部變量的局部函數或不捕獲任何局部變量的閉包可以轉換為 C 調用約定。其余的 Swift 函數不能轉換成 C 調用約定。一個 Objective-C 塊調用約定的函數不能轉換成 C 調用約定。
### `escaping` {#escaping}
在函數或者方法聲明上使用該特性,它表示參數將不會被存儲以供延遲執行。這將確保參數不會超出函數調用的生命周期。在使用 `escaping` 特性聲明的函數類型中訪問屬性和方法時需要顯式地使用 `self.`。關于如何使用 `escaping` 特性的例子,請參閱 [逃逸閉包](../02_language_guide/07_Closures.md#escaping-closures)。
## Switch Case 特性 {#switch-case-attributes}
你只能在 switch cases 語句中使用 switch case 特性。
### `unknown` {#unknown}
該特性用于 switch case,用于沒有匹配上代碼編譯時已知 case 的情況。有關如何使用 `unknown` 特性的示例,可參閱 [對未來枚舉的 `case` 進行 `switch`](./05_Statements.md#future-case)。
> 特性語法
>
>
>
#### attribute {#attribute}
>
> *特性* → @ [特性名](#attribute-name) [特性參數子句](#atribute-argument-clause)<sub>可選</sub>
>
>
#### attribute-name {#attribute-name}
>
> *特性名* → [標識符](./02_Lexical_Structure.md#identifier)
>
>
#### atribute-argument-clause {#atribute-argument-clause}
>
> *特性參數子句* → **(** [均衡令牌列表](#balanced-tokens)<sub>可選</sub> **)**
>
>
#### attributes {#attributes}
>
> *特性列表* → [特性](#attribute) [特性列表](#attributes)<sub>可選</sub>
>
>
>
#### balanced-tokens {#balanced-tokens}
>
> *均衡令牌列表* → [均衡令牌](#balanced-token) [均衡令牌列表](#balanced-tokens)<sub>可選</sub>
>
>
#### balanced-token {#balanced-token}
>
> *均衡令牌* → **(** [均衡令牌列表](#balanced-tokens)<sub>可選</sub> **)**
>
> *均衡令牌* → **\[** [均衡令牌列表](#balanced-tokens)<sub>可選</sub> **\]**
>
> *均衡令牌* → **{** [均衡令牌列表](#balanced-tokens)<sub>可選</sub> **}**
>
> *均衡令牌* → 任意標識符,關鍵字,字面量或運算符
>
> *均衡令牌* → 任意標點除了 **(**,**)**,**[**,**]**,**{**,或 **}**
>
- 1.關于 Swift
- 2.Swift 初見
- 2-1基礎部分
- 2-2基本運算符
- 2-3字符串和字符
- 2-4集合類型
- 2-5控制流
- 2-6函數
- 2-7閉包
- 2-8枚舉
- 2-9類和結構體
- 2-10屬性
- 2-11方法
- 2-12下標
- 2-13繼承
- 2-14構造過程
- 2-15析構過程
- 2-16可選鏈
- 2-17錯誤處理
- 2-18類型轉換
- 2-19嵌套類型
- 2-20擴展
- 2-21協議
- 2-22泛型
- 2-23不透明類型
- 2-24自動引用計數
- 2-25內存安全
- 2-26訪問控制
- 2-27高級運算符
- 3-1關于語言參考
- 3-2詞法結構
- 3-3類型
- 3-4表達式
- 3-5語句
- 3-6聲明
- 3-7特性
- 3-8模式
- 3-9泛型參數
- 4語法總結