一尸疆、Swift預(yù)覽
1.1 簡(jiǎn)單值
let作為常量 ?var作為變量蟀拷,常量只有在定義時(shí)賦值一次制市,可以多次使用志衍。如
let myConstant = 79 ? //常量
var myVariable = 80 ? //變量
常量和變量在設(shè)定值時(shí)必須采用相同格式。但并不需要定義精準(zhǔn)的類型聊替。創(chuàng)建一個(gè)常量或變量時(shí)提供一個(gè)值楼肪,讓編譯器判斷其類型。在上面的例子中惹悄,編譯器指定 myVariable 是一個(gè)整數(shù)春叫,因?yàn)樗某跏贾凳钦麛?shù)
若初始化時(shí)未提供足夠信息(沒(méi)有初始值),可以在變量后面指定類型泣港,用冒號(hào)隔開(kāi)暂殖。
let myConstant1 = 79
let myConstant2 = 80.0
let myConstant3 : Double = 81.0
值在轉(zhuǎn)化為另一種類型時(shí)從不具有隱含性。如果需要轉(zhuǎn)化值到另一種類型当纱,請(qǐng)明確性地為值進(jìn)行格式轉(zhuǎn)換呛每。
let label = "The width is "
let width = 94
let widthLabel = label + String(width)
更簡(jiǎn)單的方法將值轉(zhuǎn)換為String:將值寫(xiě)在括號(hào)中,并在括號(hào)前添加一個(gè)反斜杠坡氯。例
let apples = 3
let oranges = 5
let appleSummary = "I have \(apples) apples."
let fruitSummary = "I have \(apples + oranges) pieces of fruit."
通過(guò) [] 創(chuàng)建一個(gè)數(shù)組和字典晨横,通過(guò) index 和 key 獲取對(duì)應(yīng)的值
var shoppingList = ["catfish", "water", "tulips", "blue paint"]
shoppingList[1] = "bottle of water"
var occupations = [
"Malcolm": "Captain",
"Kaylee": "Mechanic",
]
occupations["Jayne"] = "Public Relations"
創(chuàng)建空數(shù)組和字典,以及初始化語(yǔ)法箫柳。
let emptyArray = String[]()
let emptyDictionary = Dictionary()
為了防止類型信息被更改手形,空數(shù)組列用[],空字典用[:]進(jìn)行初始化 - 例如滞时,為變量賦新值和給函數(shù)傳遞參數(shù)的時(shí)候叁幢。
shoppingList = []
1.2 流程控制
使用 if 和 switch 判斷條件,使用 for-in 坪稽、 for 、 while 和 do-while 處理循環(huán)鳞骤。條件和循環(huán)變量的括號(hào)可以省略窒百,語(yǔ)句體的大括號(hào)是必須的。
let personalScore = [90,129,49,28,109]
var teamScore = 0
for score in personalScore{
? ? ? ? if score > 60 {
? ? ? ? ? ? teamScore += 3
? ? ? ? }else{
? ? ? ? ? ? teamScore += 1
? ? ? ? }
}
在 if 語(yǔ)句中豫尽,條件必須是一個(gè)布爾表達(dá)式 —— 這意味著像 if score { ... } 這樣的代碼將報(bào)錯(cuò)篙梢,而不會(huì)隱形地與 0 做對(duì)比。有些變量的值是可選的美旧。一個(gè)可選的值如果是一個(gè)具體的值或者是 nil 渤滞,那表明這個(gè)值缺失。在類型后面加一個(gè) ? 來(lái)標(biāo)記這個(gè)變量的值是可選的榴嗅。
var testName: NSString? = ""
testName = nil
if? let name = testName {
? ? print("success \(name)")
}else{
? ? print("what the fuck, guy")
}
如果變量的可選值是 nil 妄呕,條件會(huì)判斷為 false ,并且大括號(hào)中的代碼會(huì)被跳過(guò)嗽测。如果不是nil绪励,會(huì)將值賦給let后面的常量肿孵,這樣代碼塊中就可以使用這個(gè)值了。
使用switch 支持任意類型的數(shù)據(jù)以及各種比較操作——不僅僅是整數(shù)以及測(cè)試相等疏魏。
let vegetable = "西紅柿"
switch vegetable {
case "土豆":
? ? print( "我就是土豆.")
case "油麥", "苦菊":
? ? print("我就是綠葉菜.")
case let x where x.hasSuffix("土"):
? ? print( "\(x) 我就是土里的")
default:
? ? print("我不在你的菜籃子里")
}
可以在循環(huán)中使用 ..< 和 ... 來(lái)表示范圍
var total = 0
// ..< 即 0到100 不包括100? ? ... 即0到100 包括100
for i in 0..<100 {
? ? if i % 2 == 0 {
? ? ? ? total += i
? ? }
}
print("100 以內(nèi)的偶數(shù)的和為\(total)")
1.3 函數(shù)與閉包
使用 func 來(lái)聲明一個(gè)函數(shù)停做,通過(guò)函數(shù)的名字和參數(shù)來(lái)調(diào)用函數(shù)。使用 -> 指定函數(shù)返回值(分離了返回值和參數(shù))
func getMaxValue(num1 : Int , num2 : Int) -> Int{
? ? if num1 > num2 {
? ? ? ? return num1
? ? }else{
? ? ? ? return num2
? ? }
}
var max = getMaxValue(num1: 100, num2: 120)
print("最大值為\(max)")
返回值為元祖
func getVegetablesPrice()->( Double , Double){
? ? return (3.35,4.35)
}
var (price1,price2) = getVegetablesPrice()
print("price1 = \(price1)? price2 = \(price2)")
//或者
var prices = getVegetablesPrice()
print("price1 = \(prices.0)? price2 = \(prices.1)")
1.4 對(duì)象與類
通過(guò)類名和 () 創(chuàng)建一個(gè)類的實(shí)例大莫,實(shí)例通過(guò)點(diǎn)語(yǔ)法訪問(wèn)屬性和方法蛉腌。
var shape = Shape()
shape.numberOfSides = 7
var shapeDescription = shape.simpleDescription()
如果子類需要重寫(xiě)父類的方法,使用 override 關(guān)鍵字只厘;如果沒(méi)有使用 override 就重寫(xiě)父類的方法烙丛,編譯器將會(huì)報(bào)錯(cuò)。同樣的編譯器會(huì)檢查 override 是否在父類中懈凹。
為了更簡(jiǎn)單的存儲(chǔ)屬性蜀变,屬性可以有 setter 和 getter 方法
var shapeLength : Int{
? ? get{
? ? ? ? return shapeLength
? ? }
? ? set{
? ? ? ? if newValue < 0? {
? ? ? ? ? ? shapeLength = 0
? ? ? ? }else{
? ? ? ? ? ? shapeLength = newValue
? ? ? ? }
? ? }
}
在shapeLength的 setter 中,新值有一個(gè)隱式的名稱是newValue介评】獗保可以在set之后的括號(hào)中提供一個(gè)明確的名稱.如果不需要計(jì)算屬性值,但是需要在設(shè)置新值之前或者之后執(zhí)行一些代碼们陆,可以通過(guò) willSet 和 didSet 完成
使用set 必須要用get 但是get 可以單獨(dú)使用(只有g(shù)et的叫只讀計(jì)算屬性)
屬性改變之前觸發(fā)willSet方法寒瓦,屬性改變之后觸發(fā)didSet方法
在給屬性添加觀察者之前必須要明確申明屬性的類型,否則編譯器會(huì)報(bào)錯(cuò)
屬性初始化時(shí)坪仇,willSet和didSet都不會(huì)調(diào)用杂腰,只有在設(shè)置屬性值時(shí)才會(huì)調(diào)用
當(dāng)設(shè)置的值和原來(lái)的值一樣時(shí),willSet和didSet也會(huì)被調(diào)用
willSet有一個(gè)newValue參數(shù)椅文,喂很,,didSet有一個(gè)newValue參數(shù)
常量和變量的命名
可以用任何喜歡的字符作為常量和變量名皆刺,包括 Unicode 字符:
let π = 3.14159
let 你好 = "你好世界"
let ???? = "dogcow"
常量與變量名不能包含數(shù)學(xué)符號(hào)少辣,箭頭,保留的(或者非法的)Unicode 碼位羡蛾,連線與制表符漓帅。也不能以數(shù)字開(kāi)頭,但是可以在常量與變量名的其他地方包含數(shù)字痴怨。
一旦將常量或者變量聲明為確定的類型忙干,就不能使用相同的名字再次進(jìn)行聲明,或者改變其存儲(chǔ)的值的類型浪藻。同時(shí)捐迫,也不能將常量與變量進(jìn)行互轉(zhuǎn)。
注意:
如果需要使用與Swift保留關(guān)鍵字相同的名稱作為常量或者變量名珠移,可以使用反引號(hào)(`)將關(guān)鍵字包圍的方式將其作為名字使用弓乙。無(wú)論如何末融,應(yīng)當(dāng)避免使用關(guān)鍵字作為常量或變量名,除非別無(wú)選擇暇韧。
可以更改現(xiàn)有的變量值為其他同類型的值勾习,在下面的例子中,friendlyWelcome的值從"Hello!"改為了"Bonjour!":
var friendlyWelcome = "Hello!"
friendlyWelcome = "Bonjour!"
// friendlyWelcome 現(xiàn)在是 "Bonjour!"
與變量不同懈玻,常量的值一旦被確定就不能更改了巧婶。嘗試這樣做會(huì)導(dǎo)致編譯時(shí)報(bào)錯(cuò):
let languageName = "Swift"
languageName = "Swift++"
// 這會(huì)報(bào)編譯時(shí)錯(cuò)誤 - languageName 不可改變
輸出常量和變量
可以用print(_:separator:terminator:)函數(shù)來(lái)輸出當(dāng)前常量或變量的值:
print(friendlyWelcome)
// 輸出 "Bonjour!"
print(_:separator:terminator:) 是一個(gè)用來(lái)輸出一個(gè)或多個(gè)值到適當(dāng)輸出區(qū)的全局函數(shù)。如果用 Xcode涂乌,print(_:separator:terminator:) 將會(huì)輸出內(nèi)容到“console”面板上艺栈。separator 和 terminator 參數(shù)具有默認(rèn)值,因此調(diào)用這個(gè)函數(shù)的時(shí)候可以忽略它們湾盒。默認(rèn)情況下湿右,該函數(shù)通過(guò)添加換行符來(lái)結(jié)束當(dāng)前行。如果不想換行罚勾,可以傳遞一個(gè)空字符串給 terminator 參數(shù)--例如毅人,print(someValue, terminator:"") 。關(guān)于參數(shù)默認(rèn)值的更多信息尖殃,請(qǐng)參考默認(rèn)參數(shù)值丈莺。
Swift 用字符串插值(string interpolation)的方式把常量名或者變量名當(dāng)做占位符加入到長(zhǎng)字符串中,Swift 會(huì)用當(dāng)前常量或變量的值替換這些占位符送丰。將常量或變量名放入圓括號(hào)中缔俄,并在開(kāi)括號(hào)前使用反斜杠將其轉(zhuǎn)義:
print("The current value of friendlyWelcome is \(friendlyWelcome)")
// 輸出 "The current value of friendlyWelcome is Bonjour!
注意:
字符串插值所有可用的選項(xiàng),請(qǐng)參考字符串插值器躏。
分號(hào)
與其他大部分編程語(yǔ)言不同俐载,Swift 并不強(qiáng)制要求在每條語(yǔ)句的結(jié)尾處使用分號(hào)(;),當(dāng)然登失,也可以按照你自己的習(xí)慣添加分號(hào)瞎疼。有一種情況下必須要用分號(hào),即打算在同一行內(nèi)寫(xiě)多條獨(dú)立的語(yǔ)句:
let cat = "??"; print(cat)
// 輸出 "??"
類型別名
類型別名(type aliases)就是給現(xiàn)有類型定義另一個(gè)名字壁畸。可以使用typealias關(guān)鍵字來(lái)定義類型別名茅茂。
當(dāng)你想要給現(xiàn)有類型起一個(gè)更有意義的名字時(shí)捏萍,類型別名非常有用。假設(shè)正在處理特定長(zhǎng)度的外部資源的數(shù)據(jù):
typealias AudioSample = UInt16
定義了一個(gè)類型別名之后空闲,可以在任何使用原始名的地方使用別名:
var maxAmplitudeFound = AudioSample.min
// maxAmplitudeFound 現(xiàn)在是 0
本例中令杈,AudioSample被定義為UInt16的一個(gè)別名。因?yàn)樗莿e名碴倾,AudioSample.min實(shí)際上是UInt16.min逗噩,所以會(huì)給maxAmplitudeFound賦一個(gè)初值0掉丽。
可選類型
使用可選類型(optionals)來(lái)處理值可能缺失的情況∫煅悖可選類型表示:
有值捶障,等于 x
或者
沒(méi)有值
注意:
C 和 Objective-C 中并沒(méi)有可選類型這個(gè)概念。最接近的是 Objective-C 中的一個(gè)特性纲刀,一個(gè)方法要不返回一個(gè)對(duì)象要不返回nil项炼,nil表示“缺少一個(gè)合法的對(duì)象”。然而示绊,這只對(duì)對(duì)象起作用——對(duì)于結(jié)構(gòu)體锭部,基本的 C 類型或者枚舉類型不起作用。對(duì)于這些類型面褐,Objective-C 方法一般會(huì)返回一個(gè)特殊值(比如NSNotFound)來(lái)暗示值缺失拌禾。這種方法假設(shè)方法的調(diào)用者知道并記得對(duì)特殊值進(jìn)行判斷。然而展哭,Swift 的可選類型可以讓你暗示任意類型的值缺失湃窍,并不需要一個(gè)特殊值。
來(lái)看一個(gè)例子摄杂。Swift 的 Int 類型有一種構(gòu)造器坝咐,作用是將一個(gè) String 值轉(zhuǎn)換成一個(gè) Int 值。然而析恢,并不是所有的字符串都可以轉(zhuǎn)換成一個(gè)整數(shù)墨坚。字符串 "123" 可以被轉(zhuǎn)換成數(shù)字 123 ,但是字符串 "hello, world" 不行映挂。
下面的例子使用這種構(gòu)造器來(lái)嘗試將一個(gè) String 轉(zhuǎn)換成 Int:
let possibleNumber = "123"
let convertedNumber = Int(possibleNumber)
// convertedNumber 被推測(cè)為類型 "Int?"泽篮, 或者類型 "optional Int"
因?yàn)樵摌?gòu)造器可能會(huì)失敗,所以它返回一個(gè)可選類型(optional)Int柑船,而不是一個(gè) Int帽撑。一個(gè)可選的 Int 被寫(xiě)作 Int? 而不是 Int。問(wèn)號(hào)暗示包含的值是可選類型鞍时,也就是說(shuō)可能包含 Int 值也可能不包含值亏拉。(不能包含其他任何值比如 Bool 值或者 String 值。只能是 Int 或者什么都沒(méi)有逆巍。)
nil
可以給可選變量賦值為nil來(lái)表示它沒(méi)有值:
var serverResponseCode: Int? = 404
// serverResponseCode 包含一個(gè)可選的 Int 值 404
serverResponseCode = nil
// serverResponseCode 現(xiàn)在不包含值
注意:
nil不能用于非可選的常量和變量及塘。如果你的代碼中有常量或者變量需要處理值缺失的情況,請(qǐng)把它們聲明成對(duì)應(yīng)的可選類型锐极。
如果你聲明一個(gè)可選常量或者變量但是沒(méi)有賦值笙僚,它們會(huì)自動(dòng)被設(shè)置為 nil:
var surveyAnswer: String?
// surveyAnswer 被自動(dòng)設(shè)置為 nil
注意:
Swift 的 nil 和 Objective-C 中的 nil 并不一樣。在 Objective-C 中灵再,nil 是一個(gè)指向不存在對(duì)象的指針肋层。在 Swift 中亿笤,nil 不是指針——它是一個(gè)確定的值,用來(lái)表示值缺失栋猖。任何類型的可選狀態(tài)都可以被設(shè)置為 nil净薛,不只是對(duì)象類型。
空合運(yùn)算符
空合運(yùn)算符(a ?? b)將對(duì)可選類型 a 進(jìn)行空判斷掂铐,如果 a 包含一個(gè)值就進(jìn)行解封罕拂,否則就返回一個(gè)默認(rèn)值 b。表達(dá)式 a 必須是 Optional 類型全陨。默認(rèn)值 b 的類型必須要和 a 存儲(chǔ)值的類型保持一致爆班。
空合運(yùn)算符是對(duì)以下代碼的簡(jiǎn)短表達(dá)方法:
a != nil ? a! : b
上述代碼使用了三目運(yùn)算符。當(dāng)可選類型 a 的值不為空時(shí)辱姨,進(jìn)行強(qiáng)制解封(a!)柿菩,訪問(wèn) a 中的值;反之返回默認(rèn)值 b雨涛。無(wú)疑空合運(yùn)算符(??)提供了一種更為優(yōu)雅的方式去封裝條件判斷和解封兩種行為枢舶,顯得簡(jiǎn)潔以及更具可讀性。
注意: 如果 a 為非空值(non-nil)替久,那么值 b 將不會(huì)被計(jì)算凉泄。這也就是所謂的短路求值。
下文例子采用空合運(yùn)算符蚯根,實(shí)現(xiàn)了在默認(rèn)顏色名和可選自定義顏色名之間抉擇:
let defaultColorName = "red"
var userDefinedColorName: String?? //默認(rèn)值為 nil
var colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 的值為空后众,所以 colorNameToUse 的值為 "red"
userDefinedColorName 變量被定義為一個(gè)可選的 String 類型,默認(rèn)值為 nil颅拦。由于 userDefinedColorName 是一個(gè)可選類型蒂誉,我們可以使用空合運(yùn)算符去判斷其值。在上一個(gè)例子中距帅,通過(guò)空合運(yùn)算符為一個(gè)名為 colorNameToUse 的變量賦予一個(gè)字符串類型初始值右锨。 由于 userDefinedColorName 值為空,因此表達(dá)式 userDefinedColorName ?? defaultColorName 返回 defaultColorName 的值碌秸,即 red绍移。
另一種情況,分配一個(gè)非空值(non-nil)給 userDefinedColorName讥电,再次執(zhí)行空合運(yùn)算登夫,運(yùn)算結(jié)果為封包在 userDefaultColorName 中的值,而非默認(rèn)值允趟。
userDefinedColorName = "green"
colorNameToUse = userDefinedColorName ?? defaultColorName
// userDefinedColorName 非空,因此 colorNameToUse 的值為 "green"
區(qū)間運(yùn)算符
閉區(qū)間運(yùn)算符
閉區(qū)間運(yùn)算符(a...b)定義一個(gè)包含從 a 到 b(包括 a 和 b)的所有值的區(qū)間鸦致。a 的值不能超過(guò) b潮剪。 ? 閉區(qū)間運(yùn)算符在迭代一個(gè)區(qū)間的所有值時(shí)是非常有用的涣楷,如在 for-in 循環(huán)中:
for index in 1...5 {
print("\(index) * 5 = \(index * 5)")
}
// 1 * 5 = 5
// 2 * 5 = 10
// 3 * 5 = 15
// 4 * 5 = 20
// 5 * 5 = 25
半開(kāi)區(qū)間運(yùn)算符
半開(kāi)區(qū)間(a..<b)定義一個(gè)從 a 到 b 但不包括 b 的區(qū)間。 之所以稱為半開(kāi)區(qū)間抗碰,是因?yàn)樵搮^(qū)間包含第一個(gè)值而不包括最后的值狮斗。
半開(kāi)區(qū)間的實(shí)用性在于當(dāng)你使用一個(gè)從 0 開(kāi)始的列表(如數(shù)組)時(shí),非常方便地從0數(shù)到列表的長(zhǎng)度弧蝇。
let names = ["Anna", "Alex", "Brian", "Jack"]
let count = names.count
for i in 0..<count {
print("第 \(i + 1) 個(gè)人叫 \(names[i])")
}
// 第 1 個(gè)人叫 Anna
// 第 2 個(gè)人叫 Alex
// 第 3 個(gè)人叫 Brian
// 第 4 個(gè)人叫 Jack
字符串索引
每一個(gè)String值都有一個(gè)關(guān)聯(lián)的索引(index)類型碳褒,String.Index,它對(duì)應(yīng)著字符串中的每一個(gè)Character的位置看疗。
前面提到沙峻,不同的字符可能會(huì)占用不同數(shù)量的內(nèi)存空間,所以要知道Character的確定位置两芳,就必須從String開(kāi)頭遍歷每一個(gè) Unicode 標(biāo)量直到結(jié)尾摔寨。因此,Swift 的字符串不能用整數(shù)(integer)做索引怖辆。
使用startIndex屬性可以獲取一個(gè)String的第一個(gè)Character的索引是复。使用endIndex屬性可以獲取最后一個(gè)Character的后一個(gè)位置的索引。因此竖螃,endIndex屬性不能作為一個(gè)字符串的有效下標(biāo)淑廊。如果String是空串,startIndex和endIndex是相等的特咆。
通過(guò)調(diào)用 String 的 index(before:) 或 index(after:) 方法季惩,可以立即得到前面或后面的一個(gè)索引。您還可以通過(guò)調(diào)用 index(_:offsetBy:) 方法來(lái)獲取對(duì)應(yīng)偏移量的索引坚弱,這種方式可以避免多次調(diào)用 index(before:) 或 index(after:) 方法蜀备。
你可以使用下標(biāo)語(yǔ)法來(lái)訪問(wèn) String 特定索引的 Character。
let greeting = "Guten Tag!"
greeting[greeting.startIndex]
// G
greeting[greeting.index(before: greeting.endIndex)]
// !
greeting[greeting.index(after: greeting.startIndex)]
// u
let index = greeting.index(greeting.startIndex, offsetBy: 7)
greeting[index]
// a
試圖獲取越界索引對(duì)應(yīng)的 Character荒叶,將引發(fā)一個(gè)運(yùn)行時(shí)錯(cuò)誤碾阁。
greeting[greeting.endIndex] // error
greeting.index(after: endIndex) // error
使用 characters 屬性的 indices 屬性會(huì)創(chuàng)建一個(gè)包含全部索引的范圍(Range),用來(lái)在一個(gè)字符串中訪問(wèn)單個(gè)字符些楣。
for index in greeting.characters.indices {
print("\(greeting[index]) ", terminator: "")
}
// 打印輸出 "G u t e n? T a g ! "
注意: 可以使用 startIndex 和 endIndex 屬性或者 index(before:) 脂凶、index(after:) 和 index(_:offsetBy:) 方法在任意一個(gè)確認(rèn)的并遵循 Collection 協(xié)議的類型里面,如上文所示是使用在 String 中愁茁,也可以使用在 Array蚕钦、Dictionary 和 Set中。
插入和刪除
調(diào)用 insert(_:atIndex:) 方法可以在一個(gè)字符串的指定索引插入一個(gè)字符鹅很,調(diào)用 insert(contentsOf:at:) 方法可以在一個(gè)字符串的指定索引插入一個(gè)段字符串嘶居。
var welcome = "hello"
welcome.insert("!", at: welcome.endIndex)
// welcome 變量現(xiàn)在等于 "hello!"
welcome.insert(contentsOf:" there".characters, at: welcome.index(before: welcome.endIndex))
// welcome 變量現(xiàn)在等于 "hello there!"
調(diào)用 remove(at:) 方法可以在一個(gè)字符串的指定索引刪除一個(gè)字符,調(diào)用 removeSubrange(_:) 方法可以在一個(gè)字符串的指定索引刪除一個(gè)子字符串。
welcome.remove(at: welcome.index(before: welcome.endIndex))
// welcome 現(xiàn)在等于 "hello there"
let range = welcome.index(welcome.endIndex, offsetBy: -6)..<welcome.endIndex
welcome.removeSubrange(range)
// welcome 現(xiàn)在等于 "hello"
注意: 可以使用 insert(_:at:)邮屁、insert(contentsOf:at:)整袁、remove(at:) 和 removeSubrange(_:) 方法在任意一個(gè)確認(rèn)的并遵循 RangeReplaceableCollection 協(xié)議的類型里面,如上文所示是使用在 String 中佑吝,也可以使用在 Array坐昙、Dictionary 和 Set 中。