Using Swift with Cocoa and Objective-C(Swift 2.0):互通性--與 C的API交互
節(jié)包含內(nèi)容:
- 基本數(shù)據(jù)類型(Primitive Types)
- 枚舉(Enumerations)
- 指針(Pointer)
- 全局常量(Global Constants)
- 預(yù)處理指令(Preprocessor Directives)
作為與Objective-C語(yǔ)言的互用性的一部分,Swift也對(duì)一些C語(yǔ)言的類型和特性保持了兼容性。如果你的代碼有需要,Swift也提供了一些方式來(lái)使用常見(jiàn)的C結(jié)構(gòu)和模式。
基本數(shù)據(jù)類型
Swift提供了一些與C語(yǔ)言基本類型如char,int,float和double等的對(duì)應(yīng)類型。然而,這些類型和Swift核心基本類型之間不能進(jìn)行隱式轉(zhuǎn)換,如Int。因此,只有你的代碼明確要求時(shí)才使用這些類型,其它任何可能的情況下都應(yīng)該使用Int。
枚舉
任何用宏NS_ENUM來(lái)聲明的C風(fēng)格的枚舉,都會(huì)被Swfit導(dǎo)入為一個(gè)Swfit枚舉類型。無(wú)論枚舉值是在系統(tǒng)框架還是在自己的代碼中定義的,當(dāng)它們導(dǎo)入到Swift時(shí),它們的前綴名將被截掉。
例如,看這個(gè)Objective-C枚舉的聲明:
//Objective-C
typedef NS_ENUM(NSInteger, UITableViewCellStyle) {
UITableViewCellStyleDefault,
UITableViewCellStyleValue1,
UITableViewCellStyleValue2,
UITableViewCellStyleSubtitle
};
在Swift中,會(huì)被導(dǎo)入為這樣:
//Swift
enum UITableViewCellStyle: Int {
case Default
case Value1
case Value2
case Subtitle
}
當(dāng)你需要使用一個(gè)枚舉值時(shí),使用以點(diǎn)(.)開(kāi)頭的枚舉名稱:
//Swift
let cellStyle: UITableViewCellStyle = .Default
選項(xiàng)集
對(duì)使用宏NS_OPTIONS聲明的C風(fēng)格的枚舉,Swift會(huì)把它導(dǎo)入為一個(gè)Swfit選項(xiàng)集類型。選項(xiàng)集像枚舉一樣,會(huì)把前綴截掉,只剩下選項(xiàng)值名稱。
例如,看這個(gè)Objective-C選項(xiàng)的聲明:
//Objective-C
typedef NS_OPTIONS(NSUInteger, NSJSONReadingOptions) {
NSJSONReadingMutableContainers = (1UL << 0),
NSJSONReadingMutableLeaves = (1UL << 1),
NSJSONReadingAllowFragments = (1UL << 2)
};
在Swift中,它被導(dǎo)入為這樣:
//Swift
struct NSJSONReadingOptions : OptionSetType {
init(rawValue: UInt)
static var MutableContainers: NSJSONReadingOptions { get }
static var MutableLeaves: NSJSONReadingOptions { get }
static var AllowFragments: NSJSONReadingOptions { get }
}
在Objective-C中,一個(gè)選項(xiàng)集是整數(shù)值的一個(gè)位掩碼。你可以使用按位或操作符(|)來(lái)組合選項(xiàng)值,使用按位與操作符(&)以檢測(cè)選項(xiàng)值。創(chuàng)建一個(gè)選項(xiàng)集,可以使用常量值或者表達(dá)式。一個(gè)空的選項(xiàng)集使用常數(shù)0來(lái)表示。
在Swift中,選項(xiàng)集使用一個(gè)遵循OptionSetType協(xié)議的結(jié)構(gòu)體來(lái)表示,其中每個(gè)選項(xiàng)值都是一個(gè)靜態(tài)變量。選項(xiàng)集類似于Swift的集合類型Set,你可以用insert(:)或者unionInPlace(:)方法來(lái)添加選項(xiàng)值,用remove(:)或者subtractInPlace(:)方法來(lái)刪除選項(xiàng)值,用contains(_:)方法來(lái)檢測(cè)選項(xiàng)值。創(chuàng)建一個(gè)選項(xiàng)集的值可以使用一個(gè)數(shù)組字面量,里面的選項(xiàng)值像枚舉一樣都用點(diǎn)(.)開(kāi)頭。創(chuàng)建一個(gè)空的選項(xiàng)集可以使用一個(gè)空的數(shù)組字面量,也可以調(diào)用默認(rèn)初始化函數(shù)。
//Swift
let options: NSDataBase64EncodingOptions = [
.Encoding76CharacterLineLength,
.EncodingEndLineWithLineFeed
]
let string = data.base64EncodedStringWithOptions(options)
共用體
Swift僅部分支持C的共用體(union)類型。在導(dǎo)入混有C的共用體或者位段(bitfields)的類型時(shí),例如Foundation的NSDecimal類型,Swift不能存取不支持的字段。但是,參數(shù)和/或返回值為這些類型的C和Objective-C的API是能夠在Swift中使用的。
指針
Swift盡可能避免讓您直接訪問(wèn)指針。然而,當(dāng)您需要直接操作內(nèi)存的時(shí)候,Swift也為您提供了多種指針類型。下面的表使用Type作為類型名稱的占位符。
對(duì)于返回類型,變量和參數(shù),使用如下形式:
對(duì)于類(class)類型,使用如下形式:
常量指針
當(dāng)一個(gè)函數(shù)被聲明為接受UnsafePointer參數(shù)時(shí),這個(gè)函數(shù)可以接受下列任何一個(gè)類型作為參數(shù):
- nil,作為空指針傳入;
- 一個(gè)UnsafePointer,UnsafeMutablePointer, 或者
AutoreleasingUnsafeMutablePointer的值,在必要情況下會(huì)轉(zhuǎn)換成UnsafePointer的值; - 一個(gè)String類型的值,如果Type是Int8或者UInt8的話。該字符串會(huì)自動(dòng)在一個(gè)緩沖區(qū)內(nèi)被轉(zhuǎn)換為UTF8,該緩沖區(qū)在本次調(diào)用期間有效;
- 一個(gè)左值操作數(shù)為Type類型的輸入輸出(inout)表達(dá)式,傳入的是這個(gè)左值的內(nèi)存地址;
- 一個(gè)[Type]值,傳入該數(shù)組的起始指針,并且它的生命周期將在本次調(diào)用期間被延長(zhǎng)。
如果您這樣定義了一個(gè)函數(shù):
//Swift
func takesAPointer(x: UnsafePointer) { /*...*/ }
那么您可以使用以下任何一種方式來(lái)調(diào)用這個(gè)函數(shù):
//Swift
var x: Float = 0.0
var p: UnsafePointer = nil
takesAPointer(nil)
takesAPointer(p)
takesAPointer(&x)
takesAPointer([1.0, 2.0, 3.0])
如果函數(shù)被聲明為使用一個(gè)UnsafePointer參數(shù),那么這個(gè)函數(shù)接受任何Type的UnsafePointer類型的操作數(shù)。 ? 如果您這樣定義了一個(gè)函數(shù):
//Swift
func takesAVoidPointer(x: UnsafePointer) { /* ... */ }
那么您可以使用以下任何一種方式來(lái)調(diào)用這個(gè)函數(shù):
//Swift
var x: Float = 0.0, y: Int = 0
var p: UnsafePointer = nil, q: UnsafePointer = nil
takesAVoidPointer(nil)
takesAVoidPointer(p)
takesAVoidPointer(q)
takesAVoidPointer(&x)
takesAVoidPointer(&y)
takesAVoidPointer([1.0, 2.0, 3.0] as [Float])
let intArray = [1, 2, 3]
takesAVoidPointer(intArray)
可變指針
當(dāng)一個(gè)方法被聲明為接受UnsafeMutablePointer參數(shù)時(shí),這個(gè)函數(shù)可以接受下列任何一個(gè)類型作為參數(shù):
- nil,作為空指針傳入;
- 一個(gè)UnsafeMutablePointer類型的值;
- 一個(gè)輸入輸出(inout)表達(dá)式,其左值操作數(shù)是Type類型的,且被存儲(chǔ)起來(lái)了。傳入的是這個(gè)左值的內(nèi)存地址;
- 一個(gè)輸入輸出的[Type]類型的值,傳入的是該數(shù)組的起始指針,并且它的生命周期將在本次調(diào)用期間被延長(zhǎng)。
如果您這樣定義了一個(gè)函數(shù):
//Swift
func takesAMutablePointer(x: UnsafeMutablePointer) { /*...*/ }
那么您可以使用以下任何一種方式來(lái)調(diào)用這個(gè)函數(shù):
//Swift
var x: Float = 0.0
var p: UnsafeMutablePointer = nil
var a: [Float] = [1.0, 2.0, 3.0]
takesAMutablePointer(nil)
takesAMutablePointer(p)
takesAMutablePointer(&x)
takesAMutablePointer(&a)
如果函數(shù)被聲明使用一個(gè)UnsafeMutablePointer參數(shù),那么這個(gè)函數(shù)接受任何Type的UnsafeMutablePointer類型的操作數(shù)。
如果您這樣定義了一個(gè)函數(shù):
//Swift
func takesAMutableVoidPointer(x: UnsafeMutablePointer) { /* ... */ }
那么您可以使用以下任何一種方式來(lái)調(diào)用這個(gè)函數(shù):
//Swift
var x: Float = 0.0, y: Int = 0
var p: UnsafeMutablePointer = nil, q: UnsafeMutablePointer = 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)
自動(dòng)釋放指針
當(dāng)一個(gè)函數(shù)被聲明為接受AutoreleasingUnsafeMutablePointer參數(shù)時(shí),這個(gè)函數(shù)可以接受下列任何一個(gè)類型作為參數(shù):
- nil,作為空指針傳入;
- 一個(gè)AutoreleasingUnsafeMutablePointer類型的值;
- 一個(gè)輸入輸出(inout)表達(dá)式,其操作數(shù)首先被拷貝到一個(gè)無(wú)擁有者的緩沖區(qū),傳遞給被調(diào)用函數(shù)的就是這個(gè)緩沖區(qū)的地址。在調(diào)用返回時(shí),緩沖區(qū)中的值被加載、保存、并重新復(fù)制給操作數(shù)。
注意,這個(gè)列表中沒(méi)有包含數(shù)組。
如果您這樣定義了一個(gè)函數(shù):
//Swift
func takesAnAutoreleasingPointer(x: AutoreleasingUnsafeMutablePointer) { /* ... */ }
那么您可以使用以下任何一種方式來(lái)調(diào)用這個(gè)函數(shù):
//Swift
var x: NSDate? = nil
var p: AutoreleasingUnsafeMutablePointer = nil
takesAnAutoreleasingPointer(nil)
takesAnAutoreleasingPointer(p)
takesAnAutoreleasingPointer(&x)
被指針指向的類型并不會(huì)被橋接。例如,NSString **轉(zhuǎn)換到Swift后,是AutoreleasingUnsafeMutablePointer,而不是AutoreleasingUnsafeMutablePointer。
函數(shù)指針
C語(yǔ)言的函數(shù)指針通過(guò)調(diào)用約定,以閉包的形式被引入Swift中,表示形式為@convention(c)。例如,一個(gè)類型為int (*)(void)的C語(yǔ)言函數(shù)指針,會(huì)轉(zhuǎn)換為Swift的@convention(c) () -> Int32。
在調(diào)用一個(gè)以函數(shù)指針為參數(shù)的函數(shù)時(shí),給它傳的值可以是一個(gè)頂層的Swift函數(shù),也可以是個(gè)閉包字面量,或者nil。只有符合C語(yǔ)言函數(shù)指針調(diào)用約定的Swift函數(shù),才能用來(lái)給函數(shù)指針類型的形參傳值。例如,Core Foundation的CFArrayCreateMutable(::_:)函數(shù),它有個(gè)參數(shù)的類型為CFArrayCallBacks結(jié)構(gòu)體。這個(gè)CFArrayCallBacks結(jié)構(gòu)體就是用一些函數(shù)指針進(jìn)行初始化的:
func customCopyDescription(p: UnsafePointer) -> Unmanaged! {
// return an Unmanaged! value
}
let callbacks = CFArrayCallBacks(
version: 0 as CFIndex,
retain: nil,
release: nil,
copyDescription: customCopyDescription,
equal: { (p1, p2) -> Boolean in
// return Boolean value
}
)
var mutableArray = CFArrayCreateMutable(nil, 0, callbacks)
在上面的例子中,在CFArrayCallBacks初始化時(shí),傳給retain和release作參數(shù)的是nil,傳給copyDescription作參數(shù)的是函數(shù)customCopyDescription,傳給equal作參數(shù)的是一個(gè)閉包字面量。
全局常量
在C和Objective-C語(yǔ)言源文件中定義的全局常量會(huì)自動(dòng)地被Swift編譯引進(jìn)并做為Swift的全局常量。
預(yù)處理指令
Swift編譯器不包含預(yù)處理器。取而代之的是,它充分利用了編譯時(shí)屬性,生成配置,和語(yǔ)言特性來(lái)完成相同的功能。因此,Swift沒(méi)有引進(jìn)預(yù)處理指令。
簡(jiǎn)單宏
在C和Objective-C中,通常使用#define指令來(lái)定義一個(gè)簡(jiǎn)單的常數(shù),在Swift,您可以使用全局常量來(lái)代替。例如:定義一個(gè)常數(shù)的#define FADE_ANIMATION_DURATION 0.35,在Swift使用let FADE_ANIMATION_DURATION = 0.35來(lái)表述會(huì)更好一些。由于簡(jiǎn)單的用于定義常量的宏會(huì)被直接被映射成Swift全局量,Swift編譯器會(huì)自動(dòng)引進(jìn)在C或Objective-C源文件中定義的簡(jiǎn)單宏。
復(fù)雜宏
在C和Objective-C中使用的復(fù)雜宏在Swift中沒(méi)有相對(duì)應(yīng)的東西。復(fù)雜宏是那些不用來(lái)定義常量的宏,包含了括號(hào)的函數(shù)式宏。您在C和Objective-C使用復(fù)雜的宏以避免類型檢查的限制或避免重新鍵入大量的樣板代碼。然而,宏也會(huì)造成debug和重構(gòu)起來(lái)更困難。在Swift中你可以使用函數(shù)和泛型來(lái)達(dá)到同樣的效果,而沒(méi)有任何的委屈折中。因此,在C和Objective-C源文件中定義的復(fù)雜宏在Swift是不能使用的。
生成配置
Swift代碼使用和C、Objective-C代碼不同的方式進(jìn)行條件編譯。Swift代碼可以根據(jù)生成配置的組合進(jìn)行條件編譯。生成配置包括true和false字面值,命令行標(biāo)志,和下表中的平臺(tái)測(cè)試函數(shù)。您可以使用-D <#flag#>指定命令行標(biāo)志。
注意:生成配置arch(arm)不會(huì)為64位ARM設(shè)備返回true,生成配置arch(i386)在為32位iOS模擬器編譯代碼時(shí)會(huì)返回true。
一個(gè)簡(jiǎn)單的條件編譯可以像下面這段代碼:
#if build configuration
statements
#else
statements
#endif
由零個(gè)或多個(gè)有效的Swift語(yǔ)句組成的statements,可以包括表達(dá)式,普通語(yǔ)句和控制流語(yǔ)句。可以使用&&和||操作符往一個(gè)條件編譯語(yǔ)句上添加新的編譯條件,使用!操作符來(lái)否定某條件,使用#elseif來(lái)添加編譯塊:
#if build configuration && !build configuration
statements
#elseif build configuration
statements
#else
statements
#endif
條件編譯不同的是,Swift條件編譯的語(yǔ)句必須是獨(dú)立完整、語(yǔ)法有效的代碼塊。這是因?yàn)樗械腟wift代碼都會(huì)做語(yǔ)法檢查,而不管會(huì)不會(huì)被編譯。