作為一種可與 Objective-C 相互調用的語言,Swift 也具有一些與 C 語言的類型和特性,如果你的代碼有需要,Swift 也提供了和常見的 C 代碼結構混合編程的編程方式。
基本數據類型
Swift 提供了一些和 C 語言的基本類型如char,int,float,double等價的 Swift 基本數據類型。然而,這些 Swift 的核心基本類型之間并不能隱式的相互轉換,如 Int。因此,只有你的代碼明確要求它們時再使用這些類型,而 Int 可以在任何你想使用它的時候使用。
C 類型 | Swift 類型 |
---|---|
bool | CBool |
char, signed char | CChar |
unsigned char | CUnsignedChar |
short | CShort |
unsigned short | CUnsignedShort |
int | CInt |
unsigned int | CUnsignedInt |
long | CLong |
unsigned long | CUnsignedLong |
long long | CLongLong |
unsigned long long | CUnsignedLongLong |
wchar_t | CWideChar |
char16_t | CChar16 |
char32_t | CChar32 |
float | CFloat |
double | CDouble |
枚舉
Swift 引進了用宏NS_ENUM來標記的任何 C 風格的枚舉類型。這意味著無論枚舉值是在系統框架還是在自定義的代碼中定義的,當他們導入到 Swift 時,他們的前綴名稱將被截斷。例如,看這個 Objective-C 枚舉:
//Objective-C
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
在 Swift 中這樣來實現:
//Swift
enum UITableViewCellStyle: Int {
case Default
case Value1
case Value2
case Subtitle
}
當您需要指向一個枚舉值時,使用以點(.)開頭的枚舉名稱:
//Swift
let cellStyle: UITableViewCellStyle = .Default
Swift 也引進了標有NS_OPTIONS宏選項。而選項的行為類似于引進的枚舉,選項還可以支持一些位操作,如 &,| 和 ~。在 Objective-C 中,你用一個空的選項設置標示恒為零(0)。在 Swift 中,使用 nil代表沒有任何選項。
指針
Swift 盡可能避免讓您直接訪問指針。然而,當您需要直接操作內存的時候,Swift 也為您提供了多種指針類型。下面的表使用 Type 作為占位符類型名稱來表示語法的映射。
對于參數,使用以下映射:
C 句法 | Swift 句法 |
---|---|
const void * | CConstVoidPointer |
void * | CMutableVoidPointer |
const Type * | CConstPointer<Type> |
Type * | CMutablePointer<Type> |
對于返回類型,變量和參數類型的多層次指針,使用以下映射:
C 句法 | Swift 句法 |
---|---|
void * | COpaquePointer |
Type * | UnsafePointer<Type> |
對于類(class)類型,使用以下映射:
C 句法 | Swift 句法 |
---|---|
Type * const * | CConstPointer<Type> |
Type * __strong * | CMutablePointer<Type> |
Type ** | AutoreleasingUnsafePointer<Type> |
C 可變指針
當一個函數被聲明為接受CMutablePointer<Type>參數時,這個函數可以接受下列任何一個類型作為參數:
•nil,作為空指針傳入
•一個CMutablePointer<Type>類型的值
•一個操作數是 Type 類型的左值的輸入輸出表達式,作為這個左值的內存地址傳入
•一個輸入輸出 Type[] 值,作為一個數組的起始指針傳入,并且它的生命周期將在這個調用期間被延長
如果您像這樣聲明了一個函數:
//Swift
func takesAMutablePointer(x: CMutablePointer<Float>) { /*...*/ }
那么您可以使用以下任何一種方式來調用這個函數:
//Swift
var x: Float = 0.0
var p: CMutablePointer<Float> = nil
var a: Float[] = [1.0, 2.0, 3.0]
takesAMutablePointer(nil)
takesAMutablePointer(p)
takesAMutablePointer(&x)
takesAMutablePointer(&a)
當函數被聲明使用一個CMutableVoidPointer參數,那么這個函數接受任何和CMutablePointer<Type>相似類型的Type操作數。
如果您這樣定義了一個函數:
//Swift
func takesAMutableVoidPointer(x: CMutableVoidPointer) { /* ... */ }
那么您可以使用以下任何一種方式來調用這個函數:
var x: Float = 0.0, y: Int = 0
var p: CMutablePointer<Float> = nil, q: CMutablePointer<Int> = nil
var a: Float[] = [1.0, 2.0, 3.0], b: Int = [1, 2, 3]
takesAMutableVoidPointer(nil)
takesAMutableVoidPointer(p)
takesAMutableVoidPointer(q)
takesAMutableVoidPointer(&x)
takesAMutableVoidPointer(&y)
takesAMutableVoidPointer(&a)
takesAMutableVoidPointer(&b)
C 常指針
當一個函數被聲明為接受CConstPointer<Type>參數時,這個函數可以接受下列任何一個類型作為參數:
•nil,作為空指針傳入
•一個CMutablePointer<Type>, CMutableVoidPointer, CConstPointer<Type>, CConstVoidPointer, 或者在必要情況下轉換成CConstPointer<Type>的AutoreleasingUnsafePointer<Type>值
•一個操作數是 Type 類型的左值的輸入輸出表達式,作為這個左值的內存地址傳入
•一個Type[]數組值,作為一個數組的起始指針傳入,并且它的生命周期將在這個調用期間被延長
//Swift
func takesAConstPointer(x: CConstPointer<Float>) { /*...*/ }
那么您可以使用以下任何一種方式來調用這個函數:
//Swift
var x: Float = 0.0
var p: CConstPointer<Float> = nil
takesAConstPointer(nil)
takesAConstPointer(p)
takesAConstPointer(&x)
takesAConstPointer([1.0, 2.0, 3.0])
當函數被聲明使用一個CConstVoidPointer參數,那么這個函數接受任何和CConstPointer<Type> 相似類型的Type操作數。 ? 如果您這樣定義了一個函數:
//Swift ???
func takesAConstVoidPointer(x: CConstVoidPointer) { /* ... */ }
那么您可以使用以下任何一種方式來調用這個函數:
//Swift
var x: Float = 0.0, y: Int = 0
var p: CConstPointer<Float> = nil, q: CConstPointer<Int> = nil
takesAConstVoidPointer(nil)
takesAConstVoidPointer(p)
takesAConstVoidPointer(q)
takesAConstVoidPointer(&x)
takesAConstVoidPointer(&y)
takesAConstVoidPointer([1.0, 2.0, 3.0])
takesAConstVoidPointer([1, 2, 3])
自動釋放不安全指針
當一個函數被聲明為接受AutoreleasingUnsafePointer<Type>參數時,這個函數可以接受下列任何一個類型作為參數:
•nil,作為空指針傳入
•一個AutoreleasingUnsafePointer<Type>值
•其操作數是原始的,復制到一個臨時的沒有所有者的緩沖區的一個輸入輸出表達式,該緩沖區的地址傳遞給調用,并返回時,緩沖區中的值加載,保存,并重新分配到操作數。
注意:這個列表沒有包含數組。
如果您這樣定義了一個函數:
//Swift
func takesAnAutoreleasingPointer(x: AutoreleasingUnsafePointer<NSDate?>) { /* ... */ }
那么您可以使用以下任何一種方式來調用這個函數:
//Swift
var x: NSDate? = nil
var p: AutoreleasingUnsafePointer<NSDate?> = nil
?????
takesAnAutoreleasingPointer(nil)
takesAnAutoreleasingPointer(p)
takesAnAutoreleasingPointer(&x)
注意:C 語言函數指針沒有被 Swift 引進。
全局常量
在 C 和 Objective-C 語言源文件中定義的全局常量會自動地被 Swift 編譯引進并做為 Swift 的全局常量。
預處理指令
Swift 編譯器不包含預處理器。取而代之的是,它充分利用了編譯時屬性,生成配置,和語言特性來完成相同的功能。因此,Swift 沒有引進預處理指令。
簡單宏
在 C 和 Objective-C,您通常使用的#define指令定義的一個宏常數,在 Swift,您可以使用全局常量來代替。例如:一個全局定義#define FADE_ANIMATION_DURATION 0.35,在 Swift 可以使用let FADE_ANIMATION_DURATION = 0.35來更好的表述。由于簡單的用于定義常量的宏會被直接被映射成 Swift 全局量,Swift 編譯器會自動引進在 C 或 Objective-C 源文件中定義的簡單宏。
復雜宏
在 C 和 Objective-C 中使用的復雜宏在 Swift 中并沒有與之對應的定義。復雜宏是那些不用來定義常量的宏,而是用來定義包含小括號(),函數的宏。您在 C 和 Objective-C 使用復雜的宏是用來避免類型檢查的限制和相同代碼的重復勞動。然而,宏也會產生Bug和重構的困難。在 Swift 中你可以直接使用函數和泛型來達到同樣的效果。因此,在 C 和 Objective-C 源文件中定義的復雜宏在 Swift 是不能使用的。
編譯配置
Swift 代碼和 Objective-C 代碼以不同的方式進行條件編譯。Swift 代碼可以根據生成配置的評價配進行有條件的編譯。生成配置包括 true 和 false 字面值,命令行標志,和下表中的平臺測試函數。您可以使用-D <#Flag#>指定命令行標志。
函數 | 有效參數 |
---|---|
os() | OSX, iOS |
arch() | x86_64, arm, arm64, i386 |
注意:arch(arm) 的生成配置不會為64位 arm 設備返回true,當代碼運行在為32位的 ios 模擬器器時,arch(i386) 的生成配置返回true。
一個簡單的條件編譯需要以下代碼格式:
#if build configuration
statements
#else
statements
#endif
一個由零個或多個有效的 Swift 語句聲明的statements,可以包括表達式,語句和控制流語句。您可以添加額外的構建配置要求,條件編譯說明用 && 和 | | 操作符以及 ! 操作符,添加條件控制塊用 #elseif:
#if build configuration && !build configuration
statements
#elseif build configuration
statements
#else
statements
#endif
與 C 語言編譯器的條件編譯相反,Swift 條件編譯語句必須完全是自包含和語法有效的代碼塊。這是因為 Swift 代碼即使沒有被編譯,也要全部進行語法檢查。