開篇
接觸Swift大概有1個多月的時間了,剛開始學(xué)習(xí)Swift的那段日子真是苦不堪言,面對著一個陌生的語言,真是不知道該從哪里著手開始學(xué)習(xí),所以跌跌撞撞地到處碰壁,踩坑。不過經(jīng)過了這一段時間自己的摸索,以及向各路大神請教,加上網(wǎng)上各種檢索資料,到現(xiàn)在應(yīng)該算是一腳踏入了Swift的大門了,盡管另外一只腳還在門外,但我會不斷地學(xué)習(xí)的。
因為我是從Objective-C語言轉(zhuǎn)過來學(xué)習(xí)Swift的,所以剛開始接觸Swift的時候,總想著在Swift里怎么實現(xiàn)OC的那些風(fēng)格習(xí)慣,比如說使用OC中的宏定義、PrefixHeader文件等等,我想,會OC的童鞋們在學(xué)習(xí)Swift時可能也會有和我一樣的想法,因此,在這里我將我學(xué)習(xí)Swift 的一些經(jīng)驗分享給大家,可能有些內(nèi)容我說的不準(zhǔn)確,甚至是錯誤的,還請大家?guī)臀抑赋?,另外,我有個和大家一起討論Swift3.0學(xué)習(xí)的技術(shù)交流群185826825,歡迎大家來與我們共同學(xué)習(xí)!我的本系列其它文章:
Swift3.0學(xué)習(xí)筆記(二)
寫在前面
當(dāng)嘗試著用一個自己陌生的語言,去實現(xiàn)一個自己想要達到的功能,是很令人興奮的。所以,在文章的開始,我想先用一個極其簡單的demo作為Swift學(xué)習(xí)的開始,這個demo的功能是頁面上有一個按鈕,點擊按鈕跳轉(zhuǎn)到另一個頁面。

大家看這段代碼,是不是發(fā)現(xiàn)和OC的風(fēng)格很像呢?是不是發(fā)現(xiàn)自己很容易就能看懂呢?其實我想說的是,世上無難事,只怕有心人,只要你愿意花時間去學(xué)習(xí),你就會發(fā)現(xiàn)其實他并不難。
基礎(chǔ)
-
導(dǎo)入文件###
在Swift中,同一個工程項目不需要引入各自的類文件,比如我新建了一個工程,里面有兩個ViewController,我在vc_A中希望引用vc_B的某個公開屬性,這時在我的vc_A中是不需要引入vc_B的文件的。
不在本工程內(nèi)創(chuàng)建的文件,如一些系統(tǒng)庫,或是通過CocoaPods加入到工程的,在使用時則需要引入到工程內(nèi),引入時也區(qū)別OC,簡單使用import 庫名即可,例如:
import UIKit
import ReactiveSwift
-
常量與變量###
在Swift中,使用let來表示常量,var來表示變量,所謂常量,即為不可改變的量,比如你聲明一個UIButton對象,后面不會給這個對象賦值成別的什么按鈕對象,初始化時即在內(nèi)存中給這個對象開辟了一塊空間,后面不會去改變這個對象的地址,因此,你可以這樣來創(chuàng)建這個對象:
let btn = UIButton(type: .system)
改變對btn的一些屬性設(shè)置,不會影響這個對象在內(nèi)存中的地址變化,因此,也就不需要將btn設(shè)置成var類型,這個看你的需求而定。
像在OC中常使用的NSMutableArray,在Swift中沒有類似可變數(shù)組這樣的類,可以直接聲明一個數(shù)組類型的變量來達到同樣的效果,比如這樣:
var array: Array<String>
需要說明的是,Array<String>為字符串類型的數(shù)組,關(guān)于數(shù)組后面會介紹。
-
數(shù)據(jù)類型###
大體上數(shù)據(jù)類型和OC也沒有什么差別,布爾類型的值從OC的YES/NO換成了true/false,其它值得注意的就是Swift本身是類型安全的語言,因此像在OC中習(xí)慣使用的小數(shù),比如CGFloat和Float在使用運算符進行運算時就會報類型不一致的錯誤了:

修改方法是需要將其中的一個進行類型轉(zhuǎn)換,以保證兩個進行運算的值的類型是一致的:
let a: CGFloat = 0.3
let b: Float = 0.4
let sum = Float(a) + b
值得說明的有兩點,
- Swift的變量和常量在聲明時可以不說明它的類型,編譯器會通過初始化的值對該變量或常量進行類型推斷。

通過這張圖我們可以看出來,我在聲明常量a時,并沒有指定a的數(shù)據(jù)類型,而是通過給a進行初始化賦了一個值0.3,這時候編譯器會根據(jù)初始化的值對常量a進行類型推斷,推斷出的結(jié)果是常量a是一個Double類型的常量。
- Swift中不需要
;作為句尾結(jié)束符,因此在Swift中對于空格的使用就要注意一些,比如在賦值符=的左右兩邊,都必須有至少一個空格才能正常編譯通過。
-
輸出函數(shù)###
由于Swift可以兼容OC,因此我們?nèi)钥梢岳^續(xù)使用NSLog輸出函數(shù)來進行輸出,同樣,Swift也提供了自己的輸出函數(shù),Print,這個函數(shù)中不再需要占位符了,你希望輸出一個變量類似這樣:
let name = "Shaw"
print("Hello \(name)")
或者這樣
let name = "Shaw"
print("Hello" + name)
-
可選類型###
這個可以算是Swift相較OC變化較大的內(nèi)容了,這就是你在閱讀Swift的代碼時經(jīng)常能夠看到的在一個變量的后面,跟了一個?或者!,這就跟可選類型相關(guān)。
聲明一個可選類型的變量,表示這個變量可以被賦值為nil,這個不同于OC,在OC里,所有的對象都可以被賦值為nil,在編譯時不會報錯,但是Swift不可以,如果一個對象在聲明時沒有聲明成可選類型,那么這個對象在編譯時是不允許被賦值為nil的。比如下面這樣:

這樣就是不允許的,這時,我們發(fā)現(xiàn)報的這個錯誤編譯器可以幫我們自動修正,修正后就是這個樣子了:
var x: UIImageView? = nil
可以發(fā)現(xiàn),編譯器只是幫我們在數(shù)據(jù)類型的后面增加了一個?,這樣就可以將變量x賦值為nil了。接下來我們給這個UIImageView對象賦一張圖片,像這樣:
x = UIImageView.init(image: UIImage.init(named: "abc"))
接下來,我們再聲明一個UIImage類型的常量y,并將變量x的image屬性的值賦給y,這時候我們不允許y為nil,我們這樣做:

你突然發(fā)現(xiàn)編譯器給我們報了兩個錯誤,先不要著急,讓我們來一一看這兩個錯誤都是什么,
- 第一個錯誤點在x的下面,說
可選類型的UIImageView沒有打開,你是要使用'!'或者'?'么?,這個錯誤發(fā)生的原因和之前我們在說Float和CGFloat那部分的問題是一樣的,由于Swift是類型安全的語言,因此一旦你聲明確定了一個變量或常量的類型,那么這個變量或常量無論在編譯時還是運行時都只能是這個類型的,對于剛才變量x下面的那個錯誤點,因為只有真正的UIImageView對象,才會有image屬性,而由于我們在聲明變量x的類型時,將x聲明成了可選類型,也就是允許x = nil,如果在訪問x的image屬性前,x的值是nil的話,程序運行就會崩潰,所以編譯器為了避免這個直接導(dǎo)致崩潰的問題發(fā)生,在編譯時會要求我們將可選類型的變量進行解包操作,只有解包后的變量x的數(shù)據(jù)類型才是真正的UIImageView類型,解包的方式就是在可選類型變量的后面加上?或者!即可,那么這二者的區(qū)別又是什么呢?
?表示嘗試將這個變量或常量進行解包,如果解包后x的值是nil,那么程序?qū)⒉辉偃ピL問x的image屬性;!則不同,其表示強制對x進行解包,如果發(fā)現(xiàn)解包后的x的值是nil,則程序會崩潰,因此需要慎用!。 - 明白了第一個錯誤是如何產(chǎn)生的,第二個問題也就迎刃而解了,同樣,我們在給一個UIImage類型的常量y賦值時,編譯器不允許y被賦值為nil,因此會強制要求你將
x.image后面加上!的,注意,這時候后面不可用?,原因還是由于Swift是類型安全的語言,不能嘗試對x.image進行解包,如果你解出來是個nil怎么辦?因此編譯器直接讓你強制解包,解出來是nil的話就搞崩潰你。
下面有兩種對于剛才這個問題的正確寫法,
let y: UIImage = x!.image!
let y: UIImage = (x?.image)!
對于這兩種寫法,都是正確的,只不過解包的思路有些不同,上面那種是先將x強制解包成UIImageView對象,再對他的image屬性進行強制解包;下面那種是先嘗試將x解包,然后訪問他的image屬性,最后對訪問的這個屬性進行強制解包。
說到這也許你會問,雖然大概明白了什么是可選類型,以及什么是解包,為什么要解包,但是你在開發(fā)時,仍然不可避免的忽略掉這些,不要捉急,機智的編譯器已將替大家考慮到這個問題了,他會在你寫代碼的時候,悄悄的自動為你加上這些符號,如果你真的寫錯了的話,他還能幫你自動修正,是不是發(fā)現(xiàn)這時候的Xcode真的挺可愛的呢?
-
運算符###
基本的運算符還都和OC一致,不過在使用運算符進行運算的時候需要注意類型一致,另外,在Swift3之前的版本中,支持對浮點數(shù)進行求余操作,但是Swift3不再支持了,系統(tǒng)提示使用一個方法進行代替:
var a: Float = 10.5
//a = a % 3
a = a.truncatingRemainder(dividingBy: 3)
輸出結(jié)果1.5
以下表格列出了在Swift3中支持的基本運算符,舉例: x = 10, y = 20
| 運算符 | 運算 | 結(jié)果 |
|---|---|---|
| + | x + y | 30 |
| - | x - y | -10 |
| * | x * y | 200 |
| / | x / y | 0 |
| % | x % y | 10 |
對于OC和Swift3之前的版本所支持的++和--運算,在Swift3中只支持這樣的形式:
| 實例 | 等價 | 結(jié)果 |
|---|---|---|
| x += 10 | x = x + 10 | 20 |
| y -= 1 | y = y - 1 | 19 |
Swift3中的邏輯運算符和位運算符都與OC沒有什么差異,需要注意的是,像這樣在OC中可以用位運算符中的邏輯或囊括的多個枚舉值:
[UIView animateWithDuration:0.1 delay:0 options:
UIViewAnimationOptionAutoreverse
| UIViewAnimationOptionAllowUserInteraction
animations:^ {
} completion: nil];
在Swift中寫起來要麻煩一些:
UIView.animate(withDuration: 0.1, delay: 0, options:
(UIViewAnimationOptions(rawValue:
UIViewAnimationOptions.autoreverse.rawValue
| UIViewAnimationOptions.allowUserInteraction.rawValue)),
animations: {
}, completion: nil)
另外,在Swift3中,增加了一個符號??,該符號的用法如下:
| 實例 | 等價 |
|---|---|
| let a = b ?? c | let a = b != nil ? b! : c |
舉例說明,一個函數(shù)的功能是接收一個可選字符串類型的參數(shù),返回一個字符串,如果傳進來的是nil,就將參數(shù)重新賦值成一個既定的字符串并返回,代碼如下:
func showMessage(msg: String? = nil) -> String {
let msg = msg ?? "默認(rèn)字符串"
return msg
}
Swift3.0的區(qū)間運算符:
| 實例 | 等價 | 說明 |
|---|---|---|
| 0...10 | 0 <= x <= 10 | 從0到10的閉區(qū)間 |
| 0..<10 | 0 <= x < 10 | 從0到10的左閉右開區(qū)間 |
-
數(shù)據(jù)存儲###
-
枚舉####
你可以聲明一個枚舉,像這樣:
enum ControlCMD {
case up, right, down, left
}
比如我們現(xiàn)在有個需求,滑動手指時輸出一個手指滑動的方向的英文,在Swift里我們只需要這樣實現(xiàn):
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let leftGR = UISwipeGestureRecognizer
.init(target: self, action: #selector(swipe(_:)))
leftGR.direction = .left
let rightGR = UISwipeGestureRecognizer
.init(target: self, action: #selector(swipe(_:)))
rightGR.direction = .right
let upGR = UISwipeGestureRecognizer
.init(target: self, action: #selector(swipe(_:)))
upGR.direction = .up
let downGR = UISwipeGestureRecognizer
.init(target: self, action: #selector(swipe(_:)))
downGR.direction = .down
self.view.addGestureRecognizer(leftGR)
self.view.addGestureRecognizer(rightGR)
self.view.addGestureRecognizer(upGR)
self.view.addGestureRecognizer(downGR)
}
enum ControlCMD: String {
case up, right, down, left
}
func swipe(_ sender: UISwipeGestureRecognizer) {
switch sender.direction {
case UISwipeGestureRecognizerDirection.up:
self.sendMessage(cmd: .up)
case UISwipeGestureRecognizerDirection.down:
self.sendMessage(cmd: .down)
case UISwipeGestureRecognizerDirection.left:
self.sendMessage(cmd: .left)
case UISwipeGestureRecognizerDirection.right:
self.sendMessage(cmd: .right)
default: break
}
}
func sendMessage(cmd: ControlCMD) {
print("滑動的方向" + cmd.rawValue)
}
}
注意,我在聲明枚舉時,將枚舉類型指定為String類型的枚舉,這樣,我在發(fā)送消息時就可以使用cmd.rawValue來訪問枚舉值的字符串了。
Swift中的枚舉還有一些更高級的用法,因為我暫時還沒有用過,所以也不在此描述了,以后遇上時再補充上。
-
元組####
對于元組這個概念,這是OC中所沒有的,我理解的元組是將多個值組合成為一個值,感覺有點像數(shù)組,但是元組中的值的類型可以是任意類型的,舉個例子:
let tuple = ("abc", 1, 0.5, [UIImage()])
這就是一個元組,元組中可以有多個元素,也可以只有一個元素,當(dāng)然,由于元組就是為了存儲多個值的,如果只有一個值也就不需要元組,多個值的時候,我們可以像數(shù)組那樣,通過使用序號來訪問元組中的元素,比如這樣:
