作為對 Objective-C 互用性(互操作性)的一部分,Swift提供快捷高效的方式來處理Cocoa數據類型。
Swift 會自動將一些 Objective-C 類型轉換為 Swift 類型,以及將 Swift 類型轉換為 Objective-C 類型。在 Objective-C 和 Swift 中也有一些具有互用性的數據類型。那些可轉換的數據類型或者具有互用性的數據類型被稱為bridged數據類型。舉個例子,在 Swift 中,您可以將一個Array值傳遞給一個要求為NSArray對象的方法。你也可以轉換一個 bridged 類型和它的副本。當你使用as轉換 bridged 類型或者那些由常量和變量所提供的類型時,Swift 會橋接它們的數據類型。
Swift 也提供一種簡單便捷的覆蓋方法來連接 Foundation 的數據類型,在后面的 Swift 語言中,你能在它的句法中感受到自然和統一。
字符串
Swift會在String類型和NSString類型中自動轉換。這意味著在可以使用NSString對象的地方,您可以使用一個屬于 Swift 的String類型代替它,這樣做會同時擁有它們數據類型的特點,String類型的插值,基于Swift設計的APIs以及NSString類更廣的適用范圍。因此,您幾乎不必再在你的代碼中使用NSString類。事實上,當 Swift 接入 Objective-C APIs 時,它將把所有NSString類型替換為String類型。當您在您的Objective-C代碼中使用 Swift 類時,接入的API會將所有String類型替換成NSString類型。
為了允許字符串轉換,只需接入Foundation。舉個例子,您在 Swift 的一個字符串中調用了capitalizedString--一個NSString類的方法,此后 Swift 會自動將String轉換為一個NSString對象并調用方法。這個方法甚至會返回一個 Swift 的String類型,因為它在接入的時候被替換了。
import Foundation
let greeting = "hello, world!"
let capitalizedGreeting = greeting.capitalizedString
// capitalizedGreeting: String = Hello, World!
如果您確實需要用到一個NSString對象,您可以用一個 Swift 的String值并轉換它。String類型總是可以從一個NSString對象轉換為一個Swift的String的值,因此,再沒有必要去使用一個可選的類型轉換器()as?)。您也可以再一個字符串中通過定義常量和變量來創建一個NSString對象。
import Foundation
let myString: NSString = "123"
if let integerValue = (myString as String).toInt()){
println("/(myString) is the integer /(integerValue)")
}
本地化
在Objective-C中,常用NSLocalizedString類的宏來定位一個字符串。這集合的宏包括NSLocalizedStringFromTableInBundle和NSLocalizedStringWithDefaultValue。而在Swift中,只用一個函數就可以實現跟整個NSLocalizedString集一樣的功能,即NSLocalizedString(key:tableName:bundle:value:comment:)。這個NSLocalizedString函數分別為tableName,bundle和value參數提供了一個默認值。你可以用它來替換宏。
數字
Swift會自動將已確定的數字類型Int和Float轉換為NSNumber。這樣的轉換允許你基于其中一種類型創建一個NSNumber:
let n = 42
let m: NSNumber = n
你也能傳遞一個Int類型的值,比如傳遞給一個要求為NSNumber類型的參數。同時需要注意的是,NSNumber可以包含多種不同的類型,因此您不能把它傳遞給單一的一個Int值。
下面所列出的類型都會自動轉換為NSNumber:
UInt
Float
Double
Bool
類集合
Swift 會自動將NSArray和NSDictionary類轉換為Swift里等價的類。這意味著你將受益于Swift強大的算法和得天獨厚的語法來處理集合--可互相轉換的 Foundation 和 Swift 集合類型。
數組
Swift 會在Array類型和NSArray類型中自動轉換。當你從一個 Swift 數組轉換到一個NSArray對象,轉換后的數組是一個AnyObject[]類型的數組。如果某個對象是 Objective-C 或者 Swift 類的實例,或者這個對象可以轉換成另一種類型,那么這個對象則屬于AnyObject類型的對象。你可以將任一NSArray對象轉換成一個 Swift 數組,因為所有 Objective-C 的對象都是AnyObject類型的。正因如此,Swift 的編譯器會在接入 Objective-C APIs 的時候將NSArray類替換成AnyObject[]。
當你將一個NSArray對象轉換成一個 Swift 數組后,你也可以將數組強制類型轉換成一個特定的類型。與從NSArray類轉換到AnyObject[]不同的是,從AnyObject類型的對象轉換成明確的類型并不會保證成功。由于直到運行時編譯器才知道AnyObject的對象能否被強制轉換為特定的類型,因此,從AnyObject[]轉換為SomeType[]會返回一個optional的值。舉個例子,如果你知道一個Swift數組只包含UIView類的實例(或者一個UIView類的子類),你可以將AnyObject類型的數組元素強制轉換為UIView對象。如果Swift數組中得元素在運行時不是UIView類型的對象,那么轉換則會返回nil。
let swiftyArray = foundationArray as AnyObject[]
if let downcastedSwiftArray = swiftArray as? UIView[] {
// downcastedSwiftArray contains only UIView objects
}
你也可以在for循環中將NSArray對象定向地強制轉換為特定類型的Swift數組:
for aView: UIView! in foundationArray {
// aView is of type UIView
}
注意:這種轉換是強制轉換,如果轉換不成功則會在運行時產生錯誤信息。
當你從 Swift 數組轉換為NSArray對象時,Swift 數組里的元素必須是屬于AnyObject的。例如,一個Int[]類型的 Swift 數組包含Int結構的元素。Int類型并不是一個類的實例,但由于Int類型轉換成了NSNumber類,Int類型屬于AnyObject類型的。因此,你可以將一個Int[]類型的Swift數組轉換為NSArray對象。如果 Swift 數組里的一個元素不屬于AnyObject類型,那么在運行時就會產生錯誤。
你也可以從 Swift 數組中創建一個NSArray對象。當你將一個常量或變量定義為一個NSArray對象并分配一個數組給它作為實例變量時,Swift 將會創建 NSArray對象,而不是 Swift 數組。
let schoolSupplies: NSArray = ["Pencil", "Eraser", "Notebkko"]
// schoolSupplies is an NSArray object containing NSString objects
上面的例子中,Swift 數組包含包含三個String字符串。由于從String類型轉換為NSString類,數組字面量被轉換成一個NSArray對象,并成功分配給schoolSupplies變量。
當您在 Objective-C 代碼中使用 Swift 類或者協議時,接入的API會將全部所有類型的Swift數組代替為NSArray。若您將一個NSArray對象傳遞給Swift的API并要求數組元素為一個新的類型,運行時就會產生錯誤。如果 Swift API 返回一個不能被轉換為NSArray類型的 Swift 數組,錯誤也會隨之產生。
Foundation數據類型
Swift 也提供一種簡單便捷的覆蓋方法來連接定義在 Foundation 框架中的數據類型。在NSSize和NSPoint中使用覆蓋方法,在剩下的 Swift 語言中,你能在它的句法中感受到自然和統一。比如,你可以使用如下語法創建一個NSSize類型的結構:
let size = NSSize(width: 20, height: 40)
覆蓋方法也允許你以一種自然的方式調用 Foundation 的結構函數。
let rect = NSRect(x: 50, y: 50, width: 100, height: 100)
let width = rect.width // equivalent of NSWidth(rect)
let maxX = rect.maxY // equivalent of NSMaxY(rect)
Swift可以將NSUInteger和NSInteger轉換為Int類型。這些類型都會在 Foundation APIs 中變為Int類型。在 Swift 中Int常被盡可能地用以連貫性,同時當你要求一個無符號整數類型時,UInt類型總是可使用的。
Foundation函數
在 Swift 中,NSLog可在系統控制臺輸出信息。您可以像在 Objective-C 中使用過的語法格式那樣使用此函數。
NSLog("%.7f", pi) // Logs "3.1415927" to the console
同時,Swift 也提供像print和println那樣的輸出函數。多歸于 Swift 的字符插值機制才讓這些函數簡單,粗暴,多效。這些函數不會在系統控制臺輸出信息,但在需要調用的時候卻是可用的。
Swift 中不再存在NSAssert函數,取而代之的是assert函數。
Core Foundation
Swift中的 Core Foundation 類型是一個成熟的類。當出現內存管理注釋時,Swift 會自動地管理 Core Foundation 對象的內存,這其中包括你實例化了的 Core Foundation 對象。在 Swift 中,你可以自由變換 Fundation 和 Core Foundation 類型。如果你想先轉換為橋接 Foundation 類型時,你也可以橋接一些 toll-free bridged Core Foundation 類型到 Swift 標準庫類型。
重定義類型
當 Swift 導入 Core Foundation 類型時,編譯器會重映射導入的類型名字。編譯器會從每個類型名字的末端移除Ref,這是因為所有的 Swift 類都屬于引用類型,因此后綴是多余的。
Core Foundation 中的CFTypeRef類型會對Anyobject類型重映射。所以你以前使用的CFTypeRef,現在該換成AnyObject了。
內存管理對象
在 Swift 中,從 annotated APIs 返回的 Core Foundation 對象能夠自動進行內存管理--你不再需要調用自身的CFRetain,CFRelease,或者CFAutorelease函數。如果你從自身的C函數和 Objective-C 方法中返回一個 Core Foundation 對象,你需要用CF_RETURNS_RETAINED或者CF_RETURNS_NOT_RETAINED注釋這個對象。當 Swift 代碼中包含這些 APIs 時,編譯器會在編譯時自動調用內存管理。如果你只調用那些不會間接返回 Core Foundation 對象的 annotated APIs,那么現在你可以跳過本節的剩余部分了。否則,下面我們繼續學習非托管的 Core Foundation 對象。
非托管對象
當 Swift 導入 unannotated 的APIs時,編譯器將不會自動地對返回的 Core Foundation 對象進行內存管理托管。Swift 將這些返回的 Core Foundation 對象封閉在一個Unmanaged<T>結構中。那些間接返回 Core Foundation 的對象也是非托管的。舉個例子,這里有一個 unannotated 的 C 函數:
CFStringRef StringByAddingTwoStrings(CFStringRef string1, CFStringRef string2)
這里說明了Swift是怎么導入的:
func StringByAddingTwoStrings(CFString!, CFString!) -> Unmanaged<CFString>!
假設您從 unannotated APIs 接收了非托管的對象,在使用它之前,你必須將它轉換為能夠內存管理的對象。在這方面,Swift 可以幫你進行內存管理而不用自己動手。同時,Unmanaged<T>結構也提供了兩個方法來把一個非托管對象轉換為一個可內存管理的對象--takeUnretainedValue()方法和takeRetainedValue()方法。這兩個方法會返回原始的,非封閉的對象類型。您可以根據您實際調用的APIs返回的unretained或retained的對象,來選擇哪一方法更合適。
比如,假設這里有一個 C 函數,這個函數在返回值前不會釋放CFString對象。在使用這個對象前,您使用takeUnretainedValue()函數,以將它轉換為一個能夠內存管理托管的對象。
let memoryManagedResult = StringByAddingTwoStrings(str1, str2).takeUnretainedValue()
// memoryManagedResult is a memory managed CFString
您也可以在一個非托管的對象中使用retain(),release()和autorelease()方法,但是這種做法并不值得推薦。