Swift中有多少關鍵字?
在Swift官方文檔的詞彙結構
中, 有非常多的關鍵字, 它們被用於聲明中、語句中、表達式中、類中、模式中, 還有以數字符號#
開頭的關鍵字, 以及特定上下文環境使用的關鍵字。本文中涉及的代碼可以在這裏下載代碼資源。
另外, 在特性
中還有一些關鍵字, 是以@
開頭的關鍵字。這些所有的關鍵字將在 Swift關鍵字總結上篇 和 Swift關鍵字總結下篇 兩篇文章中詳細列舉。
上篇中主要寫到不帶符號的關鍵字, 那麼本篇中將詳細寫到下面的這些關鍵字。如帶#
或者@
的關鍵字, 到底是如何使用的。
起始於數字標記(#)的關鍵字
#available
、#column
、#else
、#elseif
、#endif
、#file
、#function
、#if
、#line
、#selector
和 #sourceLocation
。
特性中的關鍵字(@)
@available
、@discardableResult
、@GKInspectable
、@nonobjc
、@objc
、@NSCopying
、@NSManaged
、@objcMembers
、@testable
、@NSApplicationMain
、@UIApplicationMain
、@IBAction
、@IBOutlet
、@IBDesignable
、@IBInspectable
、@autoclosure
、@escaping
、@convention
。
以數字符號#開頭的關鍵字
#available
Swift 擁有內置的對 API 可用性的檢查功能。編譯器在 SDK 中使用可用性信息來確保在你項目中明確的 API 都是可用的。如果你嘗試使用一個不可用的 API 的話,Swift 會在編譯時報告一個錯誤。
if #available(platform name version, ..., *) {
statements to execute if the APIs are available
} else {
fallback statements to execute if the APIs are unavailable
}
在這個通用的格式中,可用性條件接收平臺的名稱和版本列表。你可以使用 iOS,macOS 和 watchOS 來作爲平臺的名字。要說明額外的特定主版本號則使用類似 iOS 8 這樣的名字,你可以明確更小一點的版本號比如 iOS 8.3 和 macOS 10.10.3。
舉個例子:
if #available(iOS 10, macOS 10.12, *) {
// Use iOS 10 APIs on iOS, and use macOS 10.12 APIs on macOS
} else {
// Fall back to earlier iOS and macOS APIs
}
#file、#column、#line、#function
這些都是特殊字面量。字面量表達式要麼由普通字面量組成(例如字符串和數字), 要麼是數組或字典的字面量、playground 字面量,要麼就是這些特殊字面量。
Literal | Type | Value |
---|---|---|
#file | String | 它出現的位置的文件名 |
#line | Int | 它出現位置的行數 |
#column | Int | 他開始的列數 |
#function | String | 它出現的聲明 |
直接上代碼:
class SomeClass {
func logLiteral(fileName: String = #file, methodName: String = #function, lineNumber: Int = #line, column: Int = #column) {
print("\(fileName as NSString)->\(methodName)->\(lineNumber)->\(column)");
}
func excuteLog() {
logLiteral()
}
}
SomeClass().excuteLog()
在我工程中它是這個樣子的:
所以打印結果爲:
MyPlayground.playground->excuteLog()->95->19
#if、#end、#sourceLocation
編譯器控制語句允許程序改變編譯器的行爲。Swift 有兩種編譯器控制語句:編譯配置語句
和線路控制語句
。
編譯配置語句可以根據一個或多個配置來有條件地編譯代碼。
每一個編譯配置語句都以 #if
開始,#endif
結束。如下是一個簡單的編譯配置語句:
#if 編譯配置1
如果編譯配置1成立則執行這部分代碼
#elseif 編譯配置2
如果編譯配置2成立則執行這部分代碼
#else
如果編譯配置均不成立則執行這部分代碼
#endif
編譯配置可以是 true 和 false 的字面量,也可以是使用 -D 命令行標誌的標識符,或者是下列表格中的任意一個平臺檢測函數。
函數 | 可用參數 |
---|---|
os() | OSX, iOS, watchOS, tvOS, Linux |
arch() | i386, x86_64, arm, arm64 |
swift() | >= 後跟版本號 |
#if os(iOS)
print("come in one")
#endif
#if arch(x86_64)
print("come in two")
#endif
#if swift(>=4.0)
print("come in three")
#endif
// result is
// come in one
// come in two
// come in three
介紹完編譯配置語句, 然後開始介紹線路制語句。行控制語句可以爲被編譯的源代碼指定行號和文件名,從而改變源代碼的定位信息,以便進行分析和調試。
行控制語句形式如下:
#sourceLocation(file: 文件名 , line:行號)
#sourceLocation()
第一種的行控制語句會改變該語句之後的代碼中的字面量表達式 #line 和 #file 所表示的值。行號 是一個大於 0 的整形字面量,會改變 #line 表達式的值。文件名 是一個字符串字面量,會改變 #file 表達式的值。
第二種的行控制語句, #sourceLocation(),會將源代碼的定位信息重置回默認的行號和文件名。
@Attributes(特性)
特性給有關聲明或類型提供更多的信息。在 Swift 中有兩種特性,一種用於聲明,另一種用於類型。
通過在 @
符號後跟一個特性名稱和該特性可以接受的實際參數來指定一個特性。有些聲明特性通過接收參數來指定特性的更多信息以及它是如何修飾某個特定的聲明的。這些特性的參數寫在圓括號內,它們的格式由它們所屬的特性來定義:
@特性名
@特性名(特性參數)
聲明特性
聲明特性只能應用於聲明。
@available
將 available
特性用於聲明時,表示該聲明的生命週期與特定的平臺和操作系統版本有關。
參數說明:
available
特性經常與參數列表一同出現,該參數列表至少有兩個特性參數,參數之間由逗號分隔。這些參數由以下這些平臺名字中的一個起頭:iOS
,OSApplicationExtension
,macOS
,macOSApplicationExtension
,watchOS
,watchOSApplicationExtension
,tvOS
,tvOSApplicationExtension
,swift
當然,你也可以用一個星號(*
)來表示上面提到的所有平臺。 其餘的參數,可以按照任何順序出現,並且可以添加關於聲明生命週期的附加信息,包括重要事件。
-
unavailable
參數表示該聲明在指定的平臺上是無效的。 -
introduced
參數表示指定平臺從哪一版本開始引入該聲明。格式如下:
introduced=版本號
版本號由一個或多個正整數構成,由句點分隔的。
deprecated
參數表示指定平臺從哪一版本開始棄用該聲明。格式如下:
deprecated=版本號
可選的版本號由一個或多個正整數構成,由句點分隔的。省略版本號表示該聲明目前已棄用,當棄用出現時無需給出任何有關信息。如果你省略了版本號,冒號(:
)也可省略。
obsoleted
參數表示指定平臺從哪一版本開始廢棄該聲明。當一個聲明被廢棄後,它就從平臺中移除,不能再被使用。格式如下:
obsoleted=版本號
版本號由一個或多個正整數構成,由句點分隔的。
message
參數用來提供文本信息。當使用被棄用或者被廢棄的聲明時,編譯器會拋出警告或錯誤信息。格式如下:
message=信息內容
信息內容由一個字符串構成。
renamed
參數用來提供文本信息,用以表示被重命名的聲明的新名字。當使用聲明的舊名字時,編譯器會報錯提示新名字。格式如下:
renamed=新名字
新名字由一個字符串構成。
舉例說明:
你可以將renamed
參數和 unavailable
參數以及類型別名聲明組合使用,以此向用戶表示某個聲明已經被重命名。當某個聲明的名字在一個框架或者庫的不同發佈版本間發生變化時,這會相當有用。
// 首發版本
protocol MyProtocol {
// 這裏是協議定義
}
// 後續版本重命名了 MyProtocol
protocol MyRenamedProtocol {
// 這裏是協議定義
}
@available(*, unavailable, renamed:"MyRenamedProtocol")
typealias MyProtocol = MyRenamedProtocol
還可以可以簡明地表達出聲明在多個平臺上的可用性。如果 available
特性除了平臺名稱參數外,只指定了一個 introduced
參數,那麼可以使用以下簡寫語法代替:
@available(平臺名稱 版本號,*)
@available(macOS 10.12, *)
@available(iOS 10.0, macOS 10.12, *)
@available(swift 3.0.2)
@available(*, deprecated: 10.0)
@available(iOS, introduced: 2.0, deprecated: 8.0, message: "Header views are animated along with the rest of the view hierarchy")
@discardableResult
該特性用於的函數或方法聲明, 以抑制編譯器中函數或方法的返回值被調而沒有使用其結果的警告。
class WaringClass {
@discardableResult
func someWarningMethod() -> Bool {
return true
}
}
var waring = WaringClass()
waring.someWarningMethod()
@GKInspectable
用這個特性可以把一個自定義的 GameplayKit
組件屬性顯示到 SpriteKit
編輯器界面中。使用這個特性也就隱式地使用了 objc
特性
@objc
該特性用於修飾任何可以在 Objective-C 中表示的聲明。比如,非嵌套類、協議、非泛型枚舉(僅限原始值爲整型的枚舉)、類和協議中的屬性和方法(包括存取方法)、構造器、析構器以及下標運算符。objc
特性告訴編譯器這個聲明可以在 Objective-C 代碼中使用。需要注意的是添加objc
修飾符並不意味着這個方法或者屬性會變成動態派發, Swift依然可能會將其優化爲靜態調用。
標有 objc
特性的類必須繼承自 Objective-C 中定義的類。如果你將 objc
特性應用於一個類或協議,它也會隱式地應用於類或協議中兼容 Objective-C 的成員。對於標記了 objc
特性的類,編譯器會隱式地爲它的子類添加 objc
特性。標記了 objc
特性的協議不能繼承沒有標記 objc
的協議。
objc
特性同樣會在下面的情況中隱式地添加:
- 聲明是子類的重寫,並且父類的聲明有 objc 特性;
- 聲明滿足的需求來自一個擁有 objc 特性的協議;
- 聲明有
IBAction
,IBOutlet
,IBDesignable
,IBInspectable
,NSManaged
, 或者GKInspectable
特性。
如果你在一個枚舉中使用 objc
特性,枚舉名和每個成員名串聯起來,作爲枚舉成員暴露給 Objective-C 代碼。成員名首字母大寫。例如,在Swift中 Planet 枚舉成員叫做 venus ,它作爲一個叫 PlanetVenus 的成員暴露到 Objective-C 代碼中。
objc
特性可以接受一個特性實際參數,由一個標識符組成。當你想在 Objective-C 中爲 objc
特性標記的實體暴露一個不同的名字時,用這個特性。你可以把這個實際參數用在命名類,枚舉,枚舉成員,協議,方法,getter
,setter
,初始化器。下面的例子把 ExampleClass 中 enabled 屬性的 getter 作爲 isEnabled 暴露給 Objective-C 代碼,而不僅僅是屬性本身的名字。
//@objc
class ExampleClass: NSObject {
@objc var enabled: Bool {
@objc(isEnabled) get {
// Return the appropriate value
return true
}
}
}
@nonobjc
把這個特性應用到一個方法,屬性,下標,或者初始化器的聲明中,廢除一個隱式 objc
特性 。儘管有可能在 Objective-C 中表示一個聲明, nonobjc
特性告訴編譯器,使其聲明在 Objective-C 代碼中不可用。
給擴展使用這個特性與對擴展中的所有不顯式地用 objc
特性標記的成員使用是一樣的效果。
對於一個標爲 objc
特性的類中橋接的方法,你可以使用 nonobjc
特性解決其循環性,並且允許重載標爲 objc
特性的類中的方法和初始化器。
一個標記爲 nonobjc
特性的方法不能重寫標爲 objc
特性的方法。但是,一個標記爲 objc
特性的方法可以重寫一個標爲 nonobjc
特性的方法。同樣,一個標爲 nonobjc
特性的方法不能滿足一個標爲 objc
特性方法的協議需求。
@NSCopying
這個特性用於一個類的可變存儲屬性中。這個特性讓屬性值(由 copyWithZone(_:)
方法返回,而不是屬性本身的值)的拷貝合成屬性的setter
。屬性的類型必須遵循 NSCopying
協議。
從某種程度上來說, NSCopying
特性的行爲類似 Objective-C 中的 copy
屬性特性。@NSCopying
修飾的屬性必須是遵循NSCopying協議
的,而在 Swift 中, NSCopying協議
被限制給類使用。由於 Swift 中的 String
類型已經是值類型了。所以 String
不能通過擴展遵循NSCopying協議
。值類型的賦值,只可能是值的拷貝,當然,結構體裏面有對象類型,就另當別論了。
以下是一個使用@NSCopying的案例:
class Dog : NSObject, NSCopying {
var name = "no name"
var age = 0
func copy(with zone: NSZone? = nil) -> Any {
let copy = Dog()
print("copyed")
copy.name = name
copy.age = age
return copy
}
}
class Master : NSObject {
@NSCopying var pet : Dog
init(pet : Dog) {
self.pet = pet
super.init()
}
}
// create dogA
var dogA = Dog()
dogA.name = "dididi"
dogA.age = 1
// create dogB
var dogB = Dog()
dogB.name = "dadada"
dogB.age = 3
// create master of dogA
var master = Master(pet: dogA)
print(master.pet === dogA)
print(master.pet.name, master.pet.age)
// true
// dididi 1
// dogB replace dogA
master.pet = dogB
// copyed
print(master.pet === dogB)
print(master.pet.name, master.pet.age)
// false
// dadada 3
大部分情況下,@NSCopying
在 Swift 中的使用場景都是放在調用 Objective-C 中的類型時候使用。自定義類去遵循NSCopying協議
,不太符合 Swift 的類型體系:請使用結構體!
@NSManaged
該特性用於修飾 NSManagedObject
子類中的實例方法或存儲型變量屬性,表明它們的實現由 Core Data
在運行時基於相關實體描述動態提供。對於標記了 NSManaged
特性的屬性,Core Data
也會在運行時爲其提供存儲。應用這個特性也意味着objc
特性。
在與Core Data
模型中管理對象子類相關的特性或者關係的每個屬性定義之前,將@NSmanaged
特性加入。與 Objective-C 裏面的 @dynamic
特性類似,@NSManaged
特性告知 Swift 編譯器,這個屬性的存儲和實現將在運行時完成。但是,與@dynamic
不同的是,@NSManaged
特性僅在 Core Data
支持中可用。
Swift 類被命名空間化—他們侷限於被編譯的模塊中(最典型的是Target
)。 爲了使用帶 Core Data
模型的NSManagedObject
類的 Swift 子類,在模型實體監視器的類區域裏,用模塊名字作爲類名的前綴。
@testable
在導入允許測試的編譯模塊時,該特性用於修飾 import
聲明,這樣就能訪問被導入模塊中的任何標有 internal
訪問級別修飾符的實體,猶如它們被標記了 public
訪問級別修飾符。測試也可以訪問使用internal
或者public
訪問級別修飾符標記的類和類成員, 就像它們是open
訪問修飾符聲明的。
當你在寫一個有單元測試目標的應用時,你的代碼應該能被模塊訪問到以進行測試。默認情況下只有標註爲 open
或 public
的纔可以被其他模塊訪問。但是,如果你使用 @testable
屬性標註了導入的生產模塊並且用使能測試的方式編譯了這個模塊,單元測試目標就能訪問任何 internal
的實體。
@testable import someModule
@UIApplicationMain
我們先來看一個問題,創建一個基於 Objective-C 的 iOS App
工程。XCode 目錄下會自動創建一個main.m
文件。裏面的代碼段如下:
#import <UIKit/UIKit.h>
#import "AppDelegate.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}
其實上面這段代碼在OC工程的main.m
中所做的事情, 就是 Swift 的項目中引入 @UIApplicationMain
特性的作用。蘋果對 Swift 的項目進行了簡化,在 iOS App
工程的某個類上使用@UIApplicationMain
特性,就表示該類是當前應用程序的代理類。這是十分便利的,也是當你創建 Swift iOS工程時,系統會自動生成的 AppDelegate.swift
裏面,會看到以下代碼:
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
...
瞭解@UIApplicationMain
特性後,再來看一看不使用這個特性需要怎麼做呢?如果不通過@UIApplicationMain
特性的話,我們也可以通過調用 UIApplicationMain(_:_:_:)
函數並且把該類的名字作爲代理類的類型傳遞進函數,來達到同樣的效果。具體做法是,自己提供一個 main.swift
文件,並在代碼頂層調用 UIApplicationMain(_:_:_:)
函數來設置自己定義的Application
和AppDelegate
。
舉個例子,比如說我要自定義一個Application
去監聽所有的Event
,那麼我就可以通過UIApplicationMain(_:_:_:)
函數來傳入自己定義的Application
了。代碼如下:
import UIKit
class MyApplication: UIApplication {
override func sendEvent(_ event: UIEvent) {
super.sendEvent(event)
print("截獲的Event出發時間戳: \(event.timestamp)");
}
}
UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv,
NSStringFromClass(MyApplication.self),
NSStringFromClass(AppDelegate.self))
大家平時可以忽略這個特性,因爲 Xcode 已經自動幫你加上了。不用自己添加任何東西,只需要關注代理類中要去實現的相關的業務邏輯。但是對這個特性的理解,有助於你去理解整個 app 的生命週期。當然也包括下面要說的@NSApplicationMain
特性。
@NSApplicationMain
在某個類上使用@NSApplicationMain
特性表示該類是應用程序代理類,使用該特性與調用 NSApplicationMain(_:_:)
函數並且把該類的名字作爲代理類的類型傳遞給函數的效果相同。
其實@UIApplicationMain
和@NSApplicationMain
兩個特性所做的事情是一樣,唯一的區別是針對的項目不同。在OSX
桌面應用的開發中,使用@NSApplicationMain
;在 iOS App
工程中使用@UIApplicationMain
。所以當你使用Xcode 創建一個 Swift MacOS工程時,自動生成的 AppDelegate.swift
裏面,會看到以下代碼:
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
...
當然,如果你不想使用這個特性,也可以提供一個 main.swift
文件,並在代碼頂層調用NSApplicationMain(_:_:)
函數,如下所示:
import AppKit
NSApplicationMain(CommandLine.argc, CommandLine.unsafeArgv)
Interface Builder使用的聲明特性
Interface Builder
特性是 Interface Builder
用來與 Xcode 同步的聲明特性。Swift 提供了以下的 Interface Builder
特性:IBAction
,IBOutlet
,IBDesignable
,以及IBInspectable
。這些特性與 Objective-C 中對應的特性在概念上是相同的。
IBOutlet
和 IBInspectable
用於修飾一個類的屬性聲明,IBAction
特性用於修飾一個類的方法聲明,IBDesignable
用於修飾類的聲明。
IBAction
和 IBOutlet
特性都意味着objc
特性。
類型特性
類型特性只能用於修飾類型。
@autoclosure
這個特性通過把表達式自動封裝成無參數的閉包來延遲表達式的計算。它可以修飾類型爲返回表達式結果類型的無參數函數類型的函數參數。
自動閉包是一種自動創建的用來把作爲實際參數傳遞給函數的表達式打包的閉包。它不接受任何實際參數,並且當它被調用時,它會返回內部打包的表達式的值。自動閉包允許你延遲處理,因此閉包內部的代碼直到你調用它的時候纔會運行。對於有副作用或者佔用資源的代碼來說很有用,因爲它可以允許你控制代碼何時才進行求值。
函數類型的形式參數() -> T
(其中 T
是任何類型)可以應用 autoclosure
特性在其調用時隱式創建閉包。這提供了語法上方便的方式來推遲表達式的執行而不需要在調用函數時寫一個顯式的閉包。比如說一個() -> String
的閉包, 用@autoclosure
來修飾。現在你可以調用函數就像它接收了一個 String 實際參數而不是閉包,而實際參數自動地轉換爲了閉包。
下面來舉個例子, 寫一個函數, 入參中包含一個閉包, 而且這個閉包沒有形式參數:
// 1.完整閉包
printIfTrue(block: { () -> Bool in
return 2 > 1
})
// 2.閉包中括號內的省略
printIfTrue(block: { return 2 > 1 })
// 3.尾隨閉包的省略
printIfTrue(){ return 2 > 1 }
// 4.省略return
printIfTrue(){ 2 > 1 }
// 5.無入參時, 省略()
printIfTrue{2 > 1}
// 不使用自動閉包的用法, 如此調用會報錯
//printIfTrue(2 > 1)
寫一個同樣功能的自動閉包的函數:
func printIfTrueOrNot(block: @autoclosure ()-> Bool){
if block(){
print("The result is true")
}
}
// 使用自動閉包, 相當於把 2 > 1 這個表達式的bool結果, 自動轉換爲 () -> Bool
printIfTrueOrNot(block: 2 > 1)
@escaping
當閉包作爲一個實際參數傳遞給一個函數的時候,我們就說這個閉包逃逸了,因爲它可以在函數返回之後被調用。當你聲明一個接受閉包作爲形式參數的函數時,你可以在形式參數前寫 @escaping
來明確閉包是允許逃逸的。閉包可以逃逸的一種方法是被儲存在定義於函數外的變量裏。比如說,很多函數接收閉包實際參數來作爲啓動異步任務的回調。函數在啓動任務後返回,但是閉包要直到任務完成——閉包需要逃逸,以便於稍後調用。
將這個特性應用到一個方法或函數聲明的形參類型中,以指明可以存儲該形參值用於稍後執行。這意味着允許那個值超過調用它的範圍而存在。 escaping
類型特性的函數類型形參需要爲屬性或方法顯式使用 self.
。
var completionHandlers: [() -> Void] = []
func someFunctionWithEscapingClosure(completionHandler: @escaping () -> Void) {
print("#1-剛剛進逃逸閉包函數, 準備開始添加---\(completionHandlers.count)")
completionHandlers.append(completionHandler)
print("#1-執行到我這裏, 雖然已經將閉包添加進數組, 但是閉包還沒有執行---\(completionHandlers.count)")
}
func someFunctionWithNonescapingClosure(closure: () -> Void) {
print("#2-剛剛進入非非非逃逸閉包函數")
closure()
print("#2-代碼執行結束了")
}
class SomeClassd {
var x = 10
func doSomething() {
someFunctionWithEscapingClosure {
print("#1-這裏纔是真正執行傳入閉包的時刻---\(completionHandlers.count)")
self.x = 100
}
someFunctionWithNonescapingClosure {
print("#2-這裏我進行了操作")
self.x = 200
}
}
}
let instance = SomeClassd()
instance.doSomething()
print(instance.x)
// Prints "200"
completionHandlers.first?()
print(instance.x)
函數 someFunctionWithEscapingClosure(_:)
接收一個閉包作爲實際參數並且添加它到聲明在函數外部的數組裏。如果你不標記函數的形式參數爲 @escaping
,你就會遇到編譯時錯誤。這裏你可以很清楚的發現擁有逃逸閉包的函數和擁有非逃逸閉包的函數之間的區別。
讓閉包 @escaping
意味着你必須在閉包中顯式地引用 self
,比如說,下面的代碼中,傳給 someFunctionWithEscapingClosure(_:)
的閉包是一個逃逸閉包,也就是說它需要顯式地引用 self
。相反,傳給someFunctionWithNonescapingClosure(_:)
的閉包是非逃逸閉包,也就是說它可以隱式地引用 self
。
@convention
convention
該特性用於修飾函數類型,它指出了函數調用的約定。該特性總是與下面的參數之一一起出現:
1.swift
參數用於表示一個 Swift 函數引用。這是 Swift 中函數值的標準調用約定。
2.block
參數用於表示匹配 Objective-C 方法參數中的block
參數。函數值會作爲一個block
對象的引用,塊是一種 id
兼容的 Objective-C 對象,其中嵌入了調用函數。遵守 C函數的調用約定。
3.c
參數用於表示匹配 C 函數參數中的函數指針。函數值沒有上下文,不具備捕獲功能,同樣遵守 C函數的調用約定。
除了少數例外,當需要任何其他調用約定的函數時,可以使用任何調用約定的函數。非泛型全局函數,和局部函數或不捕獲任何局部變量的閉包,可以轉換爲 C 調用約定。其他 Swift 函數和帶有 Objective-C 閉包調用約束的函數不能轉換爲 C 調用約定。
具體場景
1.在 Swift 中調用包含函數指針參數的 C函數
定義了某個C函數:
CGFloat myCFunction(CGFloat (callback)(CGFloat x, CGFloat y)) {
return callback(1.1, 2.2);
}
其中 callback
是一個函數指針,需要調用者自己實現,在 Swift 中,如果需要實現callback
,供myCFunction
調用的話,有以下寫法,這裏就會用到@convention
:
let swiftCallback : @convention(c) (CGFloat, CGFloat) -> CGFloat = {
(x, y) -> CGFloat in
return x + y
}
let result = myCFunction( swiftCallback )
print(result) // 3.3
另外,還有更加簡單地直接使用閉包的做法,這裏沒有用到@convention
:
let result = myCFunction( {
(x, y) -> CGFloat in
return x + y
} )
print(result) // 3.3
2.在 Swift中調用包含 block參數的 Objective-C方法
與調用 C 的函數指針類似,要在 Swift 中調用一個含有 block 的 Objective-C 的方法時,需要使用@convention(block)定義 Swift 變量才能傳入到 Objective-C 的方法中。當然也可以直接使用閉包,這裏我們舉一個動畫方法的例子:
[UIView animateWithDuration:2 animations:^{
NSLog(@"start");
} completion:^(BOOL finished){
NSLog(@"completion");
}];
以上代碼使用了 2個block,直接使用閉包轉換成 Swift代碼:
UIView.animate(withDuration: 2, animations: {
NSLog("start")
}, completion: {
(completion) in
NSLog("completion")
})
等價使用@convention(block)的代碼如下:
let animationsBlock : @convention(block) () -> () = {
NSLog("start")
}
let completionBlock : @convention(block) (Bool) -> () = {
(completion) in
NSLog("start")
}
UIView.animate(withDuration: 2, animations: animationsBlock, completion: completionBlock)
相關資料:
https://blog.csdn.net/wangyanchang21/article/details/78928925)
copy關鍵字和NSCopying協議
@NSApplicationMain 和 @UIApplicationMain
@convention