本節內容包括:html
字符串(Strings)ios
數值(Numbers)算法
集合類(Collection Classes)swift
錯誤(Errors)api
Foundation數據類型(Foundation Data Types)數組
Foundation函數(Foundation Functions)app
Core Foundation框架
做爲對 Objective-C 互用性(互操做性)的一部分,Swift提供快捷高效的方式來處理 Cocoa 數據類型。ide
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設計的API,以及NSString類的多種功能。所以,咱們幾乎沒必要再在咱們的代碼中使用NSString類。事實上,當 Swift 接入 Objective-C API 時,它將把全部NSString類型替換爲String類型。當咱們在咱們的Objective-C代碼中使用 Swift 類時,接入的API會將全部String類型替換成NSString類型。
爲了容許字符串轉換,只需導入Foundation框架。舉個例子,咱們在 Swift的一個字符串中能夠訪問capitalizedString,這是NSString類的一個屬性,而後 Swift 會自動將String轉換爲一個NSString對象來訪問這個屬性。這個屬性甚至會返回一個 Swift的String類型,由於它在導入的時候被替換了。
1
2
3
4
|
import Foundation
let greeting =
"hello, world!"
let capitalizedGreeting = greeting.capitalizedString
// capitalizedGreeting: String = Hello, World!
|
若是咱們確實須要用到一個NSString對象,咱們能夠用一個 Swift 的String值並轉換它。String類型老是能夠從一個NSString對象轉換爲一個Swift的String的值,所以,再沒有必要去使用一個可選的類型轉換器(as?)。咱們也能夠在一個字符串中經過定義常量和變量來建立一個NSString對象。
1
2
3
4
5
6
|
import Foundation
let myString: NSString =
"123"
if
let integerValue = Int(myString as String) {
print(
"\(myString) is the integer \(integerValue)"
)
}
// prints "123 is the integer 123"
|
本地化
在Objective-C中,經常使用NSLocalizedString類的宏來定位一個字符串。這集合的宏包括NSLocalizedString,NSLocalizedStringFromTable,NSLocalizedStringFromTableInBundle,和NSLocalizedStringWithDefaultValue。而在Swift中,只用一個函數就能夠實現跟整個NSLocalizedString集同樣的功能,即NSLocalizedString(key:tableName:bundle:value:comment:)。這個NSLocalizedString函數分別爲tableName,bundle和value參數提供了一個默認值。咱們能夠用它來替換宏。
數值
Swift會自動將已肯定的數值類型Int和Float轉換爲NSNumber。這樣的轉換容許咱們基於其中一種類型建立一個NSNumber:
1
2
|
let n = 42
let m: NSNumber = n
|
咱們也能傳遞一個Int類型的值,好比傳遞給一個要求爲NSNumber類型的參數。同時須要注意的是,NSNumber能夠包含多種不一樣的類型,所以咱們不能把它傳遞給單一的一個Int值。
下面所列出的類型都會自動轉換爲NSNumber:
Int
UInt
Float
Double
Bool
集合類
Swift 會自動將NSArray、NSSet和NSDictionary類轉換爲Swift裏等價的類:Array、Set和Dictionary。這意味着咱們將受益於Swift強大的算法和得天獨厚的語法來處理集合--可互相轉換的 Foundation 和 Swift 集合類型。
數組(Arrays)
Swift 會在Array類型和NSArray類型中自動轉換。當咱們從一個 Swift 數組轉換成一個NSArray對象後,轉換後的結果則是一個[ObjectType]類型的數組。若是NSArray對象沒有指明一個確切的參數類型,那麼它將會轉換成[AnyObject]類型的Swift數組。
好比說,咱們看到如下Objective-C聲明:
1
2
3
|
@property NSArray* dates;
- (NSArray *)datesBeforeDate:(NSDate *)date;
- (void)addDatesParsedFromTimestamps:(NSArray *)timestamps;
|
那麼,轉換爲Swift,則是這樣子的:
1
2
3
|
var
dates: [NSDate]
func datesBeforeDate(date: NSDate) -> [NSDate]
func addDatesParsedFromTimestamps(timestamps: [String])
|
若是某個對象是 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。
1
2
3
4
|
let swiftyArray = foundationArray as AnyObject[]
if
let downcastedSwiftArray = swiftArray as? UIView[] {
// downcastedSwiftArray contains only UIView objects
}
|
咱們也能夠在for循環中將NSArray對象定向地強制轉換爲特定類型的Swift數組:
1
2
3
|
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 數組。
1
2
|
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 數組,錯誤也會隨之產生。
集合(Sets)
除了數組之外,Swift還會自動在Set類型和NSSet類之間進行轉換。當咱們將一個帶有參數類型的NSSet對象轉換爲Swift集合以後,獲得的結果是Set類型的集合。而若是NSSet對象沒有指明其參數類型,那麼它將會轉換爲Set類型的Swift集合。
好比說,咱們看到如下Objective-C聲明:
1
2
3
|
@property NSSet* words;
- (NSSet *)wordsMatchingPredicate:(NSPredicate *)predicate;
- (void)removeWords:(NSSet *)words;
|
那麼,轉換爲Swift,則是這個樣子的:
1
|
var
words: Setfunc wordsMatchingPredicate(predicate: NSPredicate) -> Setfunc removeWords(words: Set)
|
咱們可以將全部NSSet對象轉換爲Swift集合,由於全部的Objective-C對象均可以被轉換爲AnyObject。全部的NSSet對象都可以轉換爲Swift對象,所以Swift編譯器將會在導入Objective-C API的時候,將全部的NSSet類轉換爲Set。同理,當咱們在Objective-C中使用Swift類或者協議的時候,導入器將會將Swift集合從新映射爲Objective-C兼容的類型:NSSet對象。
當咱們將NSSet對象轉換爲Swift集合後,還能夠將集合下轉爲其餘指定類型。就如同Swift數組的下轉同樣,Swift集合的下轉不確保必定成功。對Set下轉爲指定類型的結果須要使用as?操做符,以確保其是可選值。
咱們仍然能夠直接從Swift數組字面量中直接建立一個NSSet對象,它一樣遵循上面提到的轉換規則。當咱們明確地將某個常量或者變量定義爲NSSet對象,而且使用一個數組字面量來賦值的時候,Swift將會建立一個NSSet對象,而不是Swift集合。
字典(Dictionaries)
Swift一樣能夠在Dictionary和NSDictionary類當中自動轉換。當咱們將帶有參數類型的NSDictionary對象轉換爲Swift字典以後,獲得的結果是一個[ObjectType]類型的字典。若是NSDictionary對象沒有指明參數類型,那麼它將會被轉換爲[NSObject:AnyObject]類型的Swift字典。
好比說,咱們看到如下Objective-C聲明:
1
2
3
|
@property NSDictionary* cachedData;
- (NSDictionary *)fileSizesForURLsWithSuffix:(NSString *)suffix;
- (void)setCacheExpirations:(NSDictionary *)expirations;
|
那麼,轉換爲Swift,則是這個樣子的:
1
2
3
|
var
cachedData: [NSURL: NSData]
func fileSizesForURLsWithSuffix(suffix: String) -> [NSURL: NSNumber]
func setCacheExpirations(expirations: [NSURL: NSDate])
|
咱們可以將全部的NSDictionary對象轉換爲Swift字典,由於全部的Objective-C對象都兼容AnyObject。重申一下,某個對象可以「兼容」AnyObject,指的是其是Objective-C的一個實例,或者是Swift的類,亦或者是可以轉換爲這二者之一的東西。全部的NSDictionary對象都可以轉換爲Swift字典,所以Swift編譯器會在導入Objective-C API的時候,將全部的NSDictionary類替換成[NSObject: AnyObject]。同理,當咱們在Objective-C中使用Swift類或者協議的時候,導入器將會將Swift字典從新映射爲Objective-C兼容的類型:NSDictionary對象。
當咱們將NSDictionary對象轉換爲Swift字典後,還能夠將字典下轉爲其餘指定類型。就如同Swift數組的下轉同樣,Swift字典的下轉不確保必定成功。對[NSObject: AnyObject]下轉爲指定類型的結果須要使用as?操做符,以確保其是可選值。
當咱們進行反向轉換,也就是將Swift字典轉換爲NSDictionary對象的過程當中,其鍵值都必須是某個類的實例,或者可以被轉換爲某個類的實例。
咱們仍然能夠直接從Swift數組字面量中直接建立一個NSDictionary對象,它一樣遵循上面提到的轉換規則。當咱們明確地將某個常量或者變量定義爲NSDictionary對象,而且使用一個數組字面量來賦值的時候,Swift將會建立一個NSDictionary對象,而不是Swift字典。
錯誤
Swift可以自動在ErrorType類型和NSError類之間轉換,會發生錯誤的Objective-C方法等價於Swift中的throw方法,而會發生錯誤的Swift方法經過Objecitive-C錯誤約定,也等價於產生錯誤的Objective-C方法。
實現ErrorType協議,而且使用@objc特性聲明的Swift枚舉類型,會產生一個NS_ENUM聲明和一個NSString常量,以可以在產生的頭文件中設定對應的錯誤範圍。好比說,有如下Swift枚舉聲明代碼:
1
2
3
|
@objc public enum CustomError: Int, ErrorType {
case
A, B, C
}
|
那麼,在相應的生成頭文件中的Objectivive-C聲明就是:
1
2
3
4
5
6
7
|
// Project-Swift.h
typedef SWIFT_ENUM(NSInteger, CustomError) {
CustomErrorA = 0,
CustomErrorB = 1,
CustomErrorC = 2,
};
static NSString * const CustomErrorDomain = @
"Project.CustomError"
;
|
關於更多Swift和Objective-C API中錯誤處理的相關信息,請參閱Error Handling。
Foundation數據類型
Swift 也提供一種簡單便捷的覆蓋方法來鏈接定義在 Foundation 框架中的數據類型。在CGSize和CGPoint中使用覆蓋方法,咱們就能在它的句法中感覺到Swift語言的天然和統一。好比,咱們可使用以下語法建立一個CGSize類型的結構:
1
|
let size = CGSize(width: 20, height: 40)
|
覆蓋方法也容許咱們以一種天然的方式調用 Foundation 的結構函數。
1
2
3
|
let rect = CGRect(x: 50, y: 50, width: 100, height: 100)
let width = rect.width
// equivalent of CGRectGetWidth(rect)
let maxX = rect.maxY
// equivalent of CGRectGetMaxY(rect)
|
Swift能夠將NSUInteger和NSInteger轉換爲Int類型。這些類型都會在 Foundation APIs 中變爲Int類型。在 Swift 中Int常被儘量地用以連貫性,同時當咱們要求一個無符號整數類型時,UInt類型老是可以使用的。
Foundation函數
在 Swift 中,NSLog可在系統控制檯輸出信息。咱們能夠像在 Objective-C 中使用過的語法格式那樣使用此函數。
1
2
|
NSLog(
"%.7f"
, pi)
// Logs "3.1415927" to the console
|
同時,Swift 也提供像print(_:)這樣的輸出函數。多虧於 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 標準庫類型。
重映射類型(Remapped Types)
當 Swift 導入 Core Foundation 類型時,編譯器會重映射導入的類型名字。編譯器會從每一個類型名字的末端移除 Ref,這是由於全部的 Swift 類都屬於引用類型,所以後綴是多餘的。
Core Foundation 中的CFTypeRef類型會對Anyobject類型重映射。因此咱們之前使用的CFTypeRef,如今該換成AnyObject了。
內存管理對象(Memory Managed Objects)
在 Swift 中,從 annotated APIs 返回的 Core Foundation 對象可以自動進行內存管理--咱們再也不須要調用自身的CFRetain,CFRelease,或者CFAutorelease函數。
若是咱們從自身的C函數和 Objective-C 方法中返回一個 Core Foundation 對象,咱們須要用CF_RETURNS_RETAINED或者CF_RETURNS_NOT_RETAINED註釋這個對象。咱們一樣可使用CF_IMPLICIT_BRIDGING_ENABLED和CF_IMPLICIT_BRIDGING_DISABLED宏命令來封裝C函數聲明,這些函數遵循Core Foundation的管理規定、命名規定,以便可以根據命名退導出內存管理機制能。
若是咱們只調用那些不會間接返回 Core Foundation 對象的 annotated APIs,那麼如今咱們能夠跳過本節的剩餘部分了。不然,下面咱們繼續學習非託管的 Core Foundation 對象。
非託管對象(Unmanaged Objects)
當 Swift 導入 unannotated 的APIs時,編譯器將不會自動地對返回的 Core Foundation 對象進行內存管理託管。Swift 將這些返回的 Core Foundation 對象封閉在一個Unmanaged結構中。那些間接返回 Core Foundation 的對象也是非託管的。舉個例子,這裏有一個 unannotated 的 C 函數:
1
|
CFStringRef StringByAddingTwoStrings(CFStringRef string1, CFStringRef string2)
|
這裏說明了Swift是怎麼導入的:
1
|
func StringByAddingTwoStrings(CFString!, CFString!) -> Unmanaged!
|
假設咱們從 unannotated APIs 接收了非託管的對象,在使用它以前,咱們必須將它轉換爲可以內存管理的對象。在這方面,Swift 能夠幫咱們進行內存管理而不用本身動手。同時,Unmanaged結構也提供了兩個方法來把一個非託管對象轉換爲一個可內存管理的對象--takeUnretainedValue()方法和takeRetainedValue()方法。這兩個方法會返回原始的,非封閉的對象類型。咱們能夠根據咱們實際調用的APIs返回的unretained或retained的對象,來選擇哪一方法更合適。
好比,假設這裏有一個 C 函數,這個函數在返回值前不會釋放CFString對象。在使用這個對象前,咱們使用takeUnretainedValue()函數,以將它轉換爲一個可以內存管理託管的對象。
1
2
|
let memoryManagedResult = StringByAddingTwoStrings(str1, str2).takeUnretainedValue()
// memoryManagedResult is a memory managed CFString
|
咱們也能夠在一個非託管的對象中使用retain(),release()和autorelease()方法,可是這種作法並不值得推薦。
要了解更多信息,請參考Memory Management Programming Guide for Core Foundation中的Memory Management Programming Guide for Core Foundation一節。