這里的教程為Swift官方教程中文版。

特性(Attributes)

在 Swift 中有兩種特性,分別用于修飾聲明和類型。特性提供了有關(guān)聲明和類型的更多信息。例如,使用 discardableResult 特性聲明的函數(shù),表明該函數(shù)雖然有返回值,但如果沒(méi)有使用該返回值,編譯器不會(huì)產(chǎn)生警告。

您可以通過(guò)以下方式指定一個(gè)特性,通過(guò)符號(hào) @ 后跟特性的名稱和特性接收的任何參數(shù):

@特性名

@特性名(特性參數(shù))

有些聲明特性通過(guò)接收參數(shù)來(lái)指定特性的更多信息以及它是如何修飾某個(gè)特定的聲明的。這些特性的參數(shù)寫在圓括號(hào)內(nèi),它們的格式由它們所屬的特性來(lái)定義。

聲明特性

聲明特性只能應(yīng)用于聲明。

available

available 特性用于聲明時(shí),表示該聲明的生命周期是相對(duì)于特定的平臺(tái)和操作系統(tǒng)版本。

available 特性經(jīng)常與參數(shù)列表一同出現(xiàn),該參數(shù)列表至少有兩個(gè)特性參數(shù),參數(shù)之間由逗號(hào)分隔。這些參數(shù)由以下這些平臺(tái)名字中的一個(gè)起頭:

  • iOS
  • iOSApplicationExtension
  • macOS
  • macOSApplicationExtension
  • watchOS
  • watchOSApplicationExtension
  • tvOS
  • tvOSApplicationExtension
  • swift

當(dāng)然,你也可以用一個(gè)星號(hào)(*)來(lái)表示上面提到的所有平臺(tái)。指定 Swift 版本的 available 特性參數(shù),不能使用星號(hào)表示。

其余的參數(shù),可以按照任何順序出現(xiàn),并且可以添加關(guān)于聲明生命周期的附加信息,包括重要事件。

  • unavailable 參數(shù)表示該聲明在指定的平臺(tái)上是無(wú)效的。當(dāng)指定 Swift 版本可用性時(shí)不可使用該參數(shù)。
  • introduced 參數(shù)表示指定平臺(tái)從哪一版本開(kāi)始引入該聲明。格式如下:

introduced: 版本號(hào)

版本號(hào)由一至三個(gè)正整數(shù)構(gòu)成,由句點(diǎn)分隔的。

  • deprecated 參數(shù)表示指定平臺(tái)從哪一版本開(kāi)始棄用該聲明。格式如下:

deprecated: 版本號(hào)

可選的版本號(hào)由一個(gè)或多個(gè)正整數(shù)構(gòu)成,由句點(diǎn)分隔的。省略版本號(hào)表示該聲明目前已棄用,而不提供有關(guān)棄用發(fā)生時(shí)間的任何信息。如果你省略了版本號(hào),冒號(hào)(:)也可省略。

  • obsoleted 參數(shù)表示指定平臺(tái)或語(yǔ)言從哪一版本開(kāi)始廢棄該聲明。當(dāng)一個(gè)聲明被廢棄后,它就從平臺(tái)或語(yǔ)言中移除,不能再被使用。格式如下:

obsoleted: 版本號(hào)

版本號(hào)由一至三個(gè)正整數(shù)構(gòu)成,由句點(diǎn)分隔的。

  • message 參數(shù)用來(lái)提供文本信息。當(dāng)使用被棄用或者被廢棄的聲明時(shí),編譯器會(huì)拋出該信息作為警告或錯(cuò)誤。格式如下:

message: 信息內(nèi)容

信息內(nèi)容由一個(gè)字符串構(gòu)成。

  • renamed 參數(shù)用來(lái)提供文本信息,用以表示被重命名聲明的新名字。當(dāng)使用聲明的舊名字時(shí),編譯器會(huì)報(bào)錯(cuò)并提示新名字。格式如下:

renamed: 新名字

新名字由一個(gè)字符串構(gòu)成。

你可以將帶有 renamedunavailable 參數(shù)的 available 特性應(yīng)用于類型別名聲明,如下所示,來(lái)表明框架和庫(kù)發(fā)行版本之間的聲明名稱已經(jīng)被更改。這個(gè)組合會(huì)導(dǎo)致聲明已重命名的編譯時(shí)錯(cuò)誤。

// 首發(fā)版本
protocol MyProtocol {
    // 這里是協(xié)議定義
}
// 后續(xù)版本重命名了 MyProtocol
protocol MyRenamedProtocol {
    // 這里是協(xié)議定義
}
@available(*, unavailable, renamed:"MyRenamedProtocol")
typealias MyProtocol = MyRenamedProtocol

你可以在某個(gè)聲明上使用多個(gè) available 特性,以指定該聲明在不同平臺(tái)和 Swift 版本上的可用性。如果當(dāng)前目標(biāo)與 available 特性中指定的平臺(tái)或語(yǔ)言版本不匹配時(shí),該聲明將會(huì)被忽略。如果你使用了多個(gè) available 特性,則最終效果是平臺(tái)和 Swift 可用性的組合。

如果 available 特性除了平臺(tái)名稱或者語(yǔ)言版本參數(shù)之外,只指定了一個(gè) introduced 參數(shù),那么可以使用以下簡(jiǎn)寫語(yǔ)法代替:

@available(平臺(tái)名稱 版本號(hào), *)

@available(swift 版本號(hào))

available 特性的簡(jiǎn)寫語(yǔ)法簡(jiǎn)潔的表示了多個(gè)平臺(tái)的可用性。盡管這兩種形式在功能上是相同的,但請(qǐng)盡可能地使用簡(jiǎn)寫語(yǔ)法形式。

@available(iOS 10.0, macOS 10.12, *)
class MyClass {
    // 這里是類定義
}

當(dāng)需要同時(shí)指定 Swift 版本和平臺(tái)可用性,需要使用一個(gè)單獨(dú)的 available 特性來(lái)指明 Swift 版本,以及一個(gè)或者多個(gè) available 特性來(lái)聲明平臺(tái)可用性。

@available(swift 3.0.2)
@available(macOS 10.12, *)
struct MyStruct {
    // 這里是結(jié)構(gòu)體定義
}

discardableResult

該特性用于的函數(shù)或方法聲明,以抑制編譯器中函數(shù)或方法被調(diào)用而其返回值沒(méi)有被使用時(shí)的警告。

dynamicCallable

該特性用于類、結(jié)構(gòu)體、枚舉或協(xié)議,以將該類型的實(shí)例視為可調(diào)用的函數(shù)。該類型必須實(shí)現(xiàn) dynamicallyCall(withArguments:)、dynamicallyCall(withKeywordArguments:) 方法之一,或兩者同時(shí)實(shí)現(xiàn)。

你可以調(diào)用 dynamicCallable 特性的實(shí)例,就像是調(diào)用一個(gè)任意數(shù)量參數(shù)的函數(shù)。

@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()

// 使用動(dòng)態(tài)方法調(diào)用
dial(4, 1, 1)
// 打印“Get Swift help on forums.swift.org”

dial(8, 6, 7, 5, 3, 0, 9)
// 打印“Unrecognized number”

// 直接調(diào)用底層方法
dial.dynamicallyCall(withArguments: [4, 1, 1])

dynamicallyCall(withArguments:) 方法的聲明必須至少有一個(gè)參數(shù)遵循 ExpressibleByArrayLiteral 協(xié)議,如 [Int],而返回值類型可以是任意類型。

如果實(shí)現(xiàn) dynamicallyCall(withKeywordArguments:) 方法,則可以在動(dòng)態(tài)方法調(diào)用中包含標(biāo)簽。

@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:) 方法聲明必須只有一個(gè)遵循 ExpressibleByDictionaryLiteral 協(xié)議的參數(shù),返回值可以任意類型。參數(shù)的 Key 必須遵循 ExpressibleByStringLiteral 協(xié)議。上述的示例使用 KeyValuePairs 作為參數(shù)類型,以便調(diào)用者可以傳入重復(fù)的參數(shù)標(biāo)簽,ab 在調(diào)用 repeat中多次使用。

如果你同時(shí)實(shí)現(xiàn)兩種 dynamicallyCall 方法,則當(dāng)在方法調(diào)用中包含關(guān)鍵字參數(shù)時(shí),會(huì)調(diào)用 dynamicallyCall(withKeywordArguments:) 方法,否則調(diào)用 dynamicallyCall(withArguments:) 方法。

你只能調(diào)用參數(shù)和返回值與 dynamicallyCall 方法實(shí)現(xiàn)匹配的動(dòng)態(tài)調(diào)用實(shí)例。在下面示例的調(diào)用無(wú)法編譯,因?yàn)槠?dynamicallyCall(withArguments:) 實(shí)現(xiàn)不接受 KeyValuePairs<String, String> 參數(shù)。

repeatLabels(a: "four") // Error

dynamicMemberLookup

該特性用于類、結(jié)構(gòu)體、枚舉或協(xié)議,讓其能在運(yùn)行時(shí)查找成員。該類型必須實(shí)現(xiàn) subscript(dynamicMemberLookup:) 下標(biāo)。

在顯式成員表達(dá)式中,如果指定成員沒(méi)有相應(yīng)的聲明,則該表達(dá)式被理解為對(duì)該類型的 subscript(dynamicMemberLookup:) 下標(biāo)調(diào)用,將有關(guān)該成員的信息作為參數(shù)傳遞。下標(biāo)接收參數(shù)既可以是鍵路徑,也可以是成員名稱字符串;如果你同時(shí)實(shí)現(xiàn)這兩種方式的下標(biāo)調(diào)用,那么以鍵路徑參數(shù)方式為準(zhǔn)。

subscript(dynamicMemberLookup:) 實(shí)現(xiàn)允許接收 KeyPathWritableKeyPathReferenceWritableKeyPath 類型的鍵路徑參數(shù)。它可以使用遵循 ExpressibleByStringLiteral 協(xié)議的類型作為參數(shù)來(lái)接受成員名 -- 通常情況下是 String。下標(biāo)返回值類型可以為任意類型。

按成員名進(jìn)行的動(dòng)態(tài)成員查找可用于圍繞編譯時(shí)無(wú)法進(jìn)行類型檢查的數(shù)據(jù)創(chuàng)建包裝類型,例如在將其他語(yǔ)言的數(shù)據(jù)橋接到 Swift 時(shí)。例如:

@dynamicMemberLookup
struct DynamicStruct {
    let dictionary = ["someDynamicMember": 325,
                      "someOtherMember": 787]
    subscript(dynamicMember member: String) -> Int {
        return dictionary[member] ?? 1054
    }
}
let s = DynamicStruct()

// 使用動(dòng)態(tài)成員查找
let dynamic = s.someDynamicMember
print(dynamic)
// 打印“325”

// 直接調(diào)用底層下標(biāo)
let equivalent = s[dynamicMember: "someDynamicMember"]
print(dynamic == equivalent)
// 打印“true”

根據(jù)鍵路徑來(lái)動(dòng)態(tài)地查找成員,可用于創(chuàng)建一個(gè)包裹數(shù)據(jù)的包裝類型,該類型支持在編譯時(shí)期進(jìn)行類型檢查。例如:

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

針對(duì)枚舉或者結(jié)構(gòu)體的聲明使用該特性,可以限制你對(duì)該類型的修改。它只有在編譯迭代庫(kù)時(shí)被允許使用。未來(lái)版本的庫(kù)不能通過(guò)添加、刪除或重新排序枚舉的 case 或結(jié)構(gòu)的存儲(chǔ)實(shí)例屬性來(lái)更改聲明。在未凍結(jié)的類型上,這些操作都是允許的,但是他們破壞了凍結(jié)類型的 ABI 兼容性。

注意
當(dāng)編譯器不處于迭代庫(kù)的模式,所有的結(jié)構(gòu)體和枚舉都是隱性凍結(jié),并且你不能使用該特性。

在迭代庫(kù)的模式中,與未凍結(jié)結(jié)構(gòu)體和枚舉的成員進(jìn)行交互的代碼在被編譯時(shí),允許它在不重新編譯的情況下繼續(xù)工作,即使在新版本的庫(kù)中添加、刪除或重新排序該類型的成員。編譯器用類似運(yùn)行時(shí)查找信息和添加間接層的技術(shù)使之可能。將一個(gè)枚舉或者結(jié)構(gòu)體標(biāo)記為凍結(jié)將以放棄這種靈活性為代價(jià)來(lái)獲取性能上的提升:未來(lái)版本的庫(kù)只能對(duì)類型進(jìn)行有限的更改,但編譯器可以對(duì)與類型成員交互的代碼進(jìn)行額外的優(yōu)化。

使用凍結(jié)類型,結(jié)構(gòu)體存儲(chǔ)屬性的類型以及枚舉 case 的關(guān)聯(lián)值必須是 public 或使用 usablefrominline 特性標(biāo)記。凍結(jié)結(jié)構(gòu)體的屬性不能有屬性觀察器,為存儲(chǔ)實(shí)例屬性提供初始值的表達(dá)式必須遵循與 inlinable 函數(shù)相同的限制,如 inlinable 中所述。

要在命令行上啟用迭代庫(kù)模式,請(qǐng)將 -enable-library-evolution 選項(xiàng)傳遞給 Swift 編譯器。要在 Xcode 中支持它,則將生成設(shè)置 “Build Libraries for Distribution”(BUILD_LIBRARY_FOR_DISTRIBUTION)設(shè)置為 Yes,詳情查看 Xcode 幫助文檔。

針對(duì)凍結(jié)枚舉的 switch 語(yǔ)法,不需要 default case,就像 對(duì)未來(lái)枚舉的 case 進(jìn)行 switch。在針對(duì)凍結(jié)枚舉使用 switch 語(yǔ)法時(shí)包含 default@unknown default case 將生成警告,因?yàn)樵摯a永遠(yuǎn)不會(huì)執(zhí)行。

GKInspectable

應(yīng)用此特性可將自定義 GameplayKit 組件屬性公開(kāi)到 SpriteKit 編輯器 UI。應(yīng)用此特性同時(shí)表示應(yīng)用了 objc 特性。

inlinable

該特性用于函數(shù)、方法、計(jì)算屬性、下標(biāo)、便利構(gòu)造器或析構(gòu)器的聲明,以將該聲明的實(shí)現(xiàn)公開(kāi)為模塊公開(kāi)接口的一部分。編譯器允許在調(diào)用處把 inlinable 標(biāo)記的符號(hào)替換為符號(hào)實(shí)現(xiàn)的副本。

內(nèi)聯(lián)代碼可以與任意模塊中 public 訪問(wèn)級(jí)別的符號(hào)進(jìn)行交互,同時(shí)可以與在相同模塊中標(biāo)記 usableFromInline 特性的 internal 訪問(wèn)級(jí)別的符號(hào)進(jìn)行交互。內(nèi)聯(lián)代碼不能與 privatefileprivate 級(jí)別的符號(hào)進(jìn)行交互。

該特性不能用于嵌套在函數(shù)內(nèi)的聲明,也不能用于 fileprivateprivate 訪問(wèn)級(jí)別的聲明。在內(nèi)聯(lián)函數(shù)內(nèi)定義的函數(shù)和閉包都是隱式內(nèi)聯(lián)的,即使他們不能標(biāo)記該特性。

nonobjc

針對(duì)方法、屬性、下標(biāo)、或構(gòu)造器的聲明使用該特性將覆蓋隱式的 objc 特性。nonobjc 特性告訴編譯器該聲明不能在 Objective-C 代碼中使用,即便它能在 Objective-C 中表示。

該特性用在擴(kuò)展中,與在沒(méi)有明確標(biāo)記為 objc 特性的擴(kuò)展中給每個(gè)成員添加該特性具有相同效果。

可以使用 nonobjc 特性解決標(biāo)有 objc 的類中橋接方法的循環(huán)問(wèn)題,該特性還允許對(duì)標(biāo)有 objc 的類中的構(gòu)造器和方法進(jìn)行重載。

標(biāo)有 nonobjc 特性的方法不能重寫標(biāo)有 objc 特性的方法。然而,標(biāo)有 objc 特性的方法可以重寫標(biāo)有 nonobjc 特性的方法。同樣,標(biāo)有 nonobjc 特性的方法不能滿足標(biāo)有 @objc 特性的協(xié)議中的方法要求。

NSApplicationMain

在類上使用該特性表示該類是應(yīng)用程序委托類。使用該特性與調(diào)用 NSApplicationMain(_:_:) 函數(shù)的效果相同。

如果你不想使用這個(gè)特性,可以提供一個(gè) main.swift 文件,并在代碼頂層調(diào)用 NSApplicationMain(_:_:) 函數(shù),如下所示:

import AppKit
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)

NSCopying

該特性用于修飾一個(gè)類的存儲(chǔ)型變量屬性。該特性將使屬性的設(shè)值方法使用傳入值的副本進(jìn)行賦值,這個(gè)值由傳入值的 copyWithZone(_:) 方法返回,而不是傳入值本身。該屬性的類型必需符合 NSCopying 協(xié)議。

NSCopying 特性的行為與 Objective-C 中的 copy 屬性特性相似。

NSManaged

該特性用于修飾 NSManagedObject 子類中的實(shí)例方法或存儲(chǔ)型變量屬性,表明它們的實(shí)現(xiàn)由 Core Data 在運(yùn)行時(shí)基于相關(guān)實(shí)體描述動(dòng)態(tài)提供。對(duì)于標(biāo)記了 NSManaged 特性的屬性,Core Data 也會(huì)在運(yùn)行時(shí)為其提供存儲(chǔ)。應(yīng)用這個(gè)特性也意味著 objc 特性。

objc

該特性用于修飾任何可以在 Objective-C 中表示的聲明。比如,非嵌套類、協(xié)議、非泛型枚舉(僅限原始值為整型的枚舉)、類的屬性和方法(包括存取方法)、協(xié)議以及協(xié)議中的可選成員、構(gòu)造器以及下標(biāo)運(yùn)算符。objc 特性告訴編譯器這個(gè)聲明可以在 Objective-C 代碼中使用。

該特性用在擴(kuò)展中,與在沒(méi)有明確標(biāo)記為 nonobjc 特性的擴(kuò)展中給每個(gè)成員添加該特性具有相同效果。

編譯器隱式地將 objc 特性添加到 Objective-C 中定義的任何類的子類。但是,子類不能是泛型的,并且不能繼承于任何泛型類。你可以將 objc 特性顯式添加到滿足這些條件的子類,來(lái)指定其 Objective-C 名稱,如下所述。添加了 objc 的協(xié)議不能繼承于沒(méi)有此特性的協(xié)議。

在以下情況中同樣會(huì)隱式的添加 objc 特性:

  • 父類有 objc 特性,且重寫為子類的聲明。
  • 遵循帶有 objc 特性協(xié)議的聲明。
  • 帶有 IBAction、IBSegueActionIBOutlet、IBDesignableIBInspectable、NSManagedGKInspectable 特性的聲明。

如果你將 objc 特性應(yīng)用于枚舉,每一個(gè)枚舉 case 都會(huì)以枚舉名稱和 case 名稱組合的方式暴露在 Objective-C 代碼中。實(shí)例名稱的首字母大寫。例如,在 Swift 枚舉類型 Planet 中有一個(gè)名為 Venus 的 case,該 case 暴露在 Objective-C 代碼中時(shí)叫做 PlanetVenus。

objc 特性可以接受一個(gè)可選的特性參數(shù),由標(biāo)識(shí)符構(gòu)成。當(dāng)你想把 objc 所修飾的實(shí)體以一個(gè)不同的名字暴露給 Objective-C 時(shí),你就可以使用這個(gè)特性參數(shù)。你可以使用這個(gè)參數(shù)來(lái)命名類、枚舉類型、枚舉 case、協(xié)議、方法、存取方法以及構(gòu)造器。如果你要指定類、協(xié)議或枚舉在 Objective-C 下的名稱,請(qǐng)?jiān)诿Q上包含三個(gè)字母的前綴,就像 Objective-C 編程 中的 約定描述的一樣。下面的例子把 ExampleClass 中的 enabled 屬性的取值方法暴露給 Objective-C,名字是 isEnabled,而不是它原來(lái)的屬性名。

class ExampleClass: NSObject {
    @objc var enabled: Bool {
        @objc(isEnabled) get {
            // 返回適當(dāng)?shù)闹?/span>
        }
    }
}

objcMembers

該特性用于類聲明,以將 objc 特性應(yīng)用于該類、擴(kuò)展、子類以及子類的擴(kuò)展的所有 Objective-C 兼容成員。

大多數(shù)代碼應(yīng)該使用 objc 特性,以暴露所需的聲明。如果需要暴露多個(gè)聲明,可以將其分組到添加 objc 特性的擴(kuò)展中。objcMembers 特性為大量使用 Objective-C 運(yùn)行時(shí)的內(nèi)省工具的庫(kù)提供了便利。添加不必要的 objc 特性會(huì)增加二進(jìn)制體積并影響性能。

propertyWrapper

在類、結(jié)構(gòu)體或者枚舉的聲明時(shí)使用該特性,可以讓其成為一個(gè)屬性包裝器。如果將該特性應(yīng)用在一個(gè)類型上,將會(huì)創(chuàng)建一個(gè)與該類型同名的自定義特性。將這個(gè)新的特性用于類、結(jié)構(gòu)體、枚舉的屬性,則可以通過(guò)包裝器的實(shí)例封裝對(duì)該屬性的訪問(wèn)。局部和全局變量不能使用屬性包裝器。

包裝器必須定義一個(gè) wrappedValue 實(shí)例屬性。該屬性 wrapped value 是該屬性存取方法暴露的值。大多數(shù)時(shí)候,wrappedValue 是一個(gè)計(jì)算屬性,但它可以是一個(gè)存儲(chǔ)屬性。包裝器負(fù)責(zé)定義和管理其包裝值所需的任何底層存儲(chǔ)。編譯器通過(guò)在包裝屬性的名稱前加下劃線(_)來(lái)為包裝器的實(shí)例提供同步存儲(chǔ)。例如,someProperty 的包裝器存儲(chǔ)為 _someProperty。包裝器的同步存儲(chǔ)具有 private 的訪問(wèn)控制級(jí)別。

擁有屬性包裝器的屬性可以包含 willSetdidSet 閉包,但是不能重寫編譯器合成的 getset 閉包。

Swift 為屬性包裝器的構(gòu)造函數(shù)提供了兩種形式的語(yǔ)法糖??梢栽诎b值的定義中使用賦值語(yǔ)法,將賦值語(yǔ)句右側(cè)的表達(dá)式作為值傳遞給屬性包裝器構(gòu)造函數(shù)中的 wrappedValue 參數(shù)。同樣的,你也可以為包裝器提供一些參數(shù),這些參數(shù)將會(huì)傳遞給包裝器的構(gòu)造函數(shù)。就像下面的例子,SomeStruct 中,定義 SomeWrapper 的地方各自調(diào)用了對(duì)應(yīng)的構(gòu)造函數(shù)。

@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

    // 兩個(gè)都是使用 init(wrappedValue:custom:)
    @SomeWrapper(custom: 98.7) var c = 30
    @SomeWrapper(wrappedValue: 30, custom: 98.7) var d
}

屬性包裝器中 projected value 是它可以用來(lái)暴露額外功能的第二個(gè)值。屬性包裝器的作者負(fù)責(zé)確認(rèn)其映射值的含義并定義公開(kāi)映射值的接口。若要通過(guò)屬性包裝器來(lái)映射值,請(qǐng)?jiān)诎b器的類型上定義 projectedValue 實(shí)例屬性。編譯器通過(guò)在包裝屬性的名稱前面加上美元符號(hào)($)來(lái)合成映射值的標(biāo)識(shí)符。例如,someProperty 的映射值是 $someProperty。映射值具有與原始包裝屬性相同的訪問(wèn)控制級(jí)別。

@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

該特性用于類聲明,以要求類中所有存儲(chǔ)屬性提供默認(rèn)值作為其定義的一部分。對(duì)于從中繼承的任何類都推斷出 NSManagedObject 特性。

testable

將此特性應(yīng)用于 import 聲明以導(dǎo)入該模塊,并更改其訪問(wèn)控制以簡(jiǎn)化對(duì)該模塊代碼的測(cè)試。這樣就能訪問(wèn)被導(dǎo)入模塊中的任何標(biāo)有 internal 訪問(wèn)級(jí)別修飾符的實(shí)體,猶如它們被標(biāo)記了 public 訪問(wèn)級(jí)別修飾符。測(cè)試也可以訪問(wèn)使用 internal 或者 public 訪問(wèn)級(jí)別修飾符標(biāo)記的類和類成員,就像它們是 open 訪問(wèn)修飾符聲明的。被導(dǎo)入的模塊必須以允許測(cè)試的方式編譯。

UIApplicationMain

在類上使用該特性表示該類是應(yīng)用程序委托類。使用該特性與調(diào)用 UIApplicationMain 函數(shù)并且把該類的名字作為委托類的名字傳遞給函數(shù)的效果相同。

如果你不想使用這個(gè)特性,可以提供一個(gè) main.swift 文件,并在代碼頂層調(diào)用 UIApplicationMain(_:_:_:_:) 函數(shù)。比如,如果你的應(yīng)用程序使用一個(gè)繼承于 UIApplication 的自定義子類作為主要類,你可以調(diào)用 UIApplicationMain(_:_:_:_:) 函數(shù)而不是使用該特性。

usableFromInline

該特性用于函數(shù)、方法、計(jì)算屬性、下標(biāo)、構(gòu)造器或析構(gòu)器的聲明,以在同一模塊中允許該符號(hào)用于內(nèi)聯(lián)代碼的聲明。聲明必須具有 internal 訪問(wèn)級(jí)別修飾符。被標(biāo)記為 usableFromInline 的結(jié)構(gòu)體或類它們屬性的類型只能是被標(biāo)記為 public 或者 usableFromInline 的類型。被標(biāo)記為 usableFromInline 的枚舉,它 case 的真實(shí)值或者關(guān)聯(lián)類型只能是被標(biāo)記為 public 或者 usableFromInline 的類型。

public 訪問(wèn)修飾符相同的是,該特性將聲明公開(kāi)為模塊公共接口的一部分。區(qū)別于 public,編譯器不允許在模塊外部的代碼通過(guò)名稱引用 usableFromInline 標(biāo)記的聲明,即使導(dǎo)出了聲明符號(hào)也無(wú)法引用。但是,模塊外的代碼仍然可以通過(guò)運(yùn)行時(shí)與聲明符號(hào)進(jìn)行交互。

標(biāo)記為 inlinable 特性的聲明,在內(nèi)聯(lián)代碼中可以隱式使用。雖然 inlinableusableFromInline 可以用于 internal 聲明,但這兩者不能同時(shí)使用。

warn-unqualified-access

該特性應(yīng)用于頂級(jí)函數(shù)、實(shí)例方法、類方法或靜態(tài)方法,以在沒(méi)有前置限定符(例如模塊名稱、類型名稱、實(shí)例變量或常量)的情況下使用該函數(shù)或方法時(shí)觸發(fā)警告。使用該特性可以減少在同一作用域里訪問(wèn)的同名函數(shù)之間的歧義。

例如,Swift 標(biāo)準(zhǔn)庫(kù)包含 min(_:_:) 頂級(jí)函數(shù)和用于序列比較元素的 min() 方法。序列方法聲明使用了 warn_unqualified_access,以減少在 Sequence 擴(kuò)展中使用它們的歧義。

Interface Builder 使用的聲明特性

Interface Builder 特性是 Interface Builder 用來(lái)與 Xcode 同步的聲明特性。Swift 提供了以下的 Interface Builder 特性:IBAction,IBSegueActionIBOutlet,IBDesignable,以及 IBInspectable。這些特性與 Objective-C 中對(duì)應(yīng)的特性在概念上是相同的。

IBOutletIBInspectable 用于修飾一個(gè)類的屬性聲明。IBActionIBSegueAction 特性用于修飾一個(gè)類的方法聲明,IBDesignable 用于修飾類的聲明。

應(yīng)用 IBAction、IBSegueAction、IBOutletIBDesignable 或者 IBInspectable 特性都意味著同時(shí)應(yīng)用 objc 特性。

類型特性

類型特性只能用于修飾類型。

autoclosure

這個(gè)特性通過(guò)把表達(dá)式自動(dòng)封裝成無(wú)參數(shù)的閉包來(lái)延遲表達(dá)式的計(jì)算。它可以修飾類型為返回表達(dá)式結(jié)果類型的無(wú)參數(shù)函數(shù)類型的函數(shù)參數(shù)。關(guān)于如何使用 autoclosure 特性的例子,請(qǐng)參閱 自動(dòng)閉包函數(shù)類型。

convention

該特性用于修飾函數(shù)類型,它指出了函數(shù)調(diào)用的約定。

convention 特性總是與下面的參數(shù)之一一起出現(xiàn)。

  • swift 參數(shù)用于表示一個(gè) Swift 函數(shù)引用。這是 Swift 中函數(shù)值的標(biāo)準(zhǔn)調(diào)用約定。

  • block 參數(shù)用于表示一個(gè) Objective-C 兼容的塊引用。函數(shù)值會(huì)作為一個(gè)塊對(duì)象的引用,塊是一種 id 兼容的 Objective-C 對(duì)象,其中嵌入了調(diào)用函數(shù)。調(diào)用函數(shù)使用 C 的調(diào)用約定。

  • c 參數(shù)用于表示一個(gè) C 函數(shù)引用。函數(shù)值沒(méi)有上下文,不具備捕獲功能,并且使用 C 的調(diào)用約定。

除了少數(shù)例外,當(dāng)函數(shù)需要任何其他調(diào)用約定時(shí),可以轉(zhuǎn)換成任意調(diào)用約定的函數(shù)。非范型全局函數(shù)、不捕獲任何局部變量的局部函數(shù)或不捕獲任何局部變量的閉包可以轉(zhuǎn)換為 C 調(diào)用約定。其余的 Swift 函數(shù)不能轉(zhuǎn)換成 C 調(diào)用約定。一個(gè) Objective-C 塊調(diào)用約定的函數(shù)不能轉(zhuǎn)換成 C 調(diào)用約定。

escaping

在函數(shù)或者方法聲明上使用該特性,它表示參數(shù)將不會(huì)被存儲(chǔ)以供延遲執(zhí)行。這將確保參數(shù)不會(huì)超出函數(shù)調(diào)用的生命周期。在使用 escaping 特性聲明的函數(shù)類型中訪問(wèn)屬性和方法時(shí)需要顯式地使用 self.。關(guān)于如何使用 escaping 特性的例子,請(qǐng)參閱 逃逸閉包。

Switch Case 特性

你只能在 switch cases 語(yǔ)句中使用 switch case 特性。

unknown

該特性用于 switch case,用于沒(méi)有匹配上代碼編譯時(shí)已知 case 的情況。有關(guān)如何使用 unknown 特性的示例,可參閱 對(duì)未來(lái)枚舉的 case 進(jìn)行 switch。

特性語(yǔ)法

attribute

特性 → @ 特性名 特性參數(shù)子句可選

attribute-name

特性名標(biāo)識(shí)符

atribute-argument-clause

特性參數(shù)子句( 均衡令牌列表可選 )

attributes

特性列表特性 特性列表可選

balanced-tokens

均衡令牌列表均衡令牌 均衡令牌列表可選

balanced-token

均衡令牌( 均衡令牌列表可選 )

均衡令牌[ 均衡令牌列表可選 ]

均衡令牌{ 均衡令牌列表可選 }

均衡令牌 → 任意標(biāo)識(shí)符,關(guān)鍵字,字面量或運(yùn)算符

均衡令牌 → 任意標(biāo)點(diǎn)除了 (,),[],{,或 }

? 聲明 模式 ?
?