開篇
接觸Swift大概有1個多月的時間了,剛開始學習Swift的那段日子真是苦不堪言,面對著一個陌生的語言,真是不知道該從哪里著手開始學習,所以跌跌撞撞地到處碰壁,踩坑。不過經過了這一段時間自己的摸索,以及向各路大神請教,加上網上各種檢索資料,到現在應該算是一腳踏入了Swift的大門了,盡管另外一只腳還在門外,但我會不斷地學習的。
因為我是從Objective-C語言轉過來學習Swift的,所以剛開始接觸Swift的時候,總想著在Swift里怎么實現OC的那些風格習慣,比如說使用OC中的宏定義、PrefixHeader文件等等,我想,會OC的童鞋們在學習Swift時可能也會有和我一樣的想法,因此,在這里我將我學習Swift 的一些經驗分享給大家,可能有些內容我說的不準確,甚至是錯誤的,還請大家幫我指出,另外,我有個和大家一起討論Swift3.0學習的技術交流群185826825,歡迎大家來與我們共同學習!我的本系列其它文章:
Swift3.0學習筆記(二)
寫在前面
當嘗試著用一個自己陌生的語言,去實現一個自己想要達到的功能,是很令人興奮的。所以,在文章的開始,我想先用一個極其簡單的demo作為Swift學習的開始,這個demo的功能是頁面上有一個按鈕,點擊按鈕跳轉到另一個頁面。
大家看這段代碼,是不是發現和OC的風格很像呢?是不是發現自己很容易就能看懂呢?其實我想說的是,世上無難事,只怕有心人,只要你愿意花時間去學習,你就會發現其實他并不難。
基礎
-
導入文件###
在Swift中,同一個工程項目不需要引入各自的類文件,比如我新建了一個工程,里面有兩個ViewController,我在vc_A中希望引用vc_B的某個公開屬性,這時在我的vc_A中是不需要引入vc_B的文件的。
不在本工程內創建的文件,如一些系統庫,或是通過CocoaPods加入到工程的,在使用時則需要引入到工程內,引入時也區別OC,簡單使用import 庫名
即可,例如:
import UIKit
import ReactiveSwift
-
常量與變量###
在Swift中,使用let
來表示常量,var
來表示變量,所謂常量,即為不可改變的量,比如你聲明一個UIButton
對象,后面不會給這個對象賦值成別的什么按鈕對象,初始化時即在內存中給這個對象開辟了一塊空間,后面不會去改變這個對象的地址,因此,你可以這樣來創建這個對象:
let btn = UIButton(type: .system)
改變對btn
的一些屬性設置,不會影響這個對象在內存中的地址變化,因此,也就不需要將btn
設置成var
類型,這個看你的需求而定。
像在OC中常使用的NSMutableArray,在Swift中沒有類似可變數組這樣的類,可以直接聲明一個數組類型的變量來達到同樣的效果,比如這樣:
var array: Array<String>
需要說明的是,Array<String>
為字符串類型的數組,關于數組后面會介紹。
-
數據類型###
大體上數據類型和OC也沒有什么差別,布爾類型的值從OC的YES/NO
換成了true/false
,其它值得注意的就是Swift本身是類型安全的語言,因此像在OC中習慣使用的小數,比如CGFloat和Float在使用運算符進行運算時就會報類型不一致的錯誤了:
修改方法是需要將其中的一個進行類型轉換,以保證兩個進行運算的值的類型是一致的:
let a: CGFloat = 0.3
let b: Float = 0.4
let sum = Float(a) + b
值得說明的有兩點,
- Swift的變量和常量在聲明時可以不說明它的類型,編譯器會通過初始化的值對該變量或常量進行類型推斷。
通過這張圖我們可以看出來,我在聲明常量a時,并沒有指定a的數據類型,而是通過給a進行初始化賦了一個值0.3,這時候編譯器會根據初始化的值對常量a進行類型推斷,推斷出的結果是常量a是一個Double類型的常量。
- Swift中不需要
;
作為句尾結束符,因此在Swift中對于空格
的使用就要注意一些,比如在賦值符=
的左右兩邊,都必須有至少一個空格
才能正常編譯通過。
-
輸出函數###
由于Swift可以兼容OC,因此我們仍可以繼續使用NSLog輸出函數來進行輸出,同樣,Swift也提供了自己的輸出函數,Print,這個函數中不再需要占位符了,你希望輸出一個變量類似這樣:
let name = "Shaw"
print("Hello \(name)")
或者這樣
let name = "Shaw"
print("Hello" + name)
-
可選類型###
這個可以算是Swift相較OC變化較大的內容了,這就是你在閱讀Swift的代碼時經常能夠看到的在一個變量的后面,跟了一個?
或者!
,這就跟可選類型相關。
聲明一個可選類型的變量,表示這個變量可以被賦值為nil,這個不同于OC,在OC里,所有的對象
都可以被賦值為nil,在編譯時不會報錯,但是Swift不可以,如果一個對象在聲明時沒有聲明成可選類型,那么這個對象在編譯時是不允許被賦值為nil的。比如下面這樣:
這樣就是不允許的,這時,我們發現報的這個錯誤編譯器可以幫我們自動修正,修正后就是這個樣子了:
var x: UIImageView? = nil
可以發現,編譯器只是幫我們在數據類型的后面增加了一個?
,這樣就可以將變量x賦值為nil了。接下來我們給這個UIImageView對象賦一張圖片,像這樣:
x = UIImageView.init(image: UIImage.init(named: "abc"))
接下來,我們再聲明一個UIImage類型的常量y,并將變量x的image屬性的值賦給y,這時候我們不允許y為nil,我們這樣做:
你突然發現編譯器給我們報了兩個錯誤,先不要著急,讓我們來一一看這兩個錯誤都是什么,
- 第一個錯誤點在x的下面,說
可選類型的UIImageView沒有打開,你是要使用'!'或者'?'么?
,這個錯誤發生的原因和之前我們在說Float和CGFloat那部分的問題是一樣的,由于Swift是類型安全的語言,因此一旦你聲明確定了一個變量或常量的類型,那么這個變量或常量無論在編譯時還是運行時都只能是這個類型的,對于剛才變量x下面的那個錯誤點,因為只有真正的UIImageView對象,才會有image屬性,而由于我們在聲明變量x的類型時,將x聲明成了可選類型,也就是允許x = nil
,如果在訪問x的image屬性前,x的值是nil的話,程序運行就會崩潰,所以編譯器為了避免這個直接導致崩潰的問題發生,在編譯時會要求我們將可選類型的變量進行解包
操作,只有解包后的變量x的數據類型才是真正的UIImageView類型,解包的方式就是在可選類型變量的后面加上?
或者!
即可,那么這二者的區別又是什么呢?
?
表示嘗試將這個變量或常量進行解包,如果解包后x的值是nil,那么程序將不再去訪問x的image屬性;!
則不同,其表示強制對x進行解包,如果發現解包后的x的值是nil,則程序會崩潰,因此需要慎用!
。 - 明白了第一個錯誤是如何產生的,第二個問題也就迎刃而解了,同樣,我們在給一個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
屬性,最后對訪問的這個屬性進行強制解包。
說到這也許你會問,雖然大概明白了什么是可選類型,以及什么是解包,為什么要解包,但是你在開發時,仍然不可避免的忽略掉這些,不要捉急,機智的編譯器已將替大家考慮到這個問題了,他會在你寫代碼的時候,悄悄的自動為你加上這些符號,如果你真的寫錯了的話,他還能幫你自動修正,是不是發現這時候的Xcode真的挺可愛的呢?
-
運算符###
基本的運算符還都和OC一致,不過在使用運算符進行運算的時候需要注意類型一致,另外,在Swift3之前的版本中,支持對浮點數進行求余操作,但是Swift3不再支持了,系統提示使用一個方法進行代替:
var a: Float = 10.5
//a = a % 3
a = a.truncatingRemainder(dividingBy: 3)
輸出結果1.5
以下表格列出了在Swift3中支持的基本運算符,舉例: x = 10, y = 20
運算符 | 運算 | 結果 |
---|---|---|
+ | x + y | 30 |
- | x - y | -10 |
* | x * y | 200 |
/ | x / y | 0 |
% | x % y | 10 |
對于OC和Swift3之前的版本所支持的++
和--
運算,在Swift3中只支持這樣的形式:
實例 | 等價 | 結果 |
---|---|---|
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 |
舉例說明,一個函數的功能是接收一個可選字符串類型的參數,返回一個字符串,如果傳進來的是nil,就將參數重新賦值成一個既定的字符串并返回,代碼如下:
func showMessage(msg: String? = nil) -> String {
let msg = msg ?? "默認字符串"
return msg
}
Swift3.0的區間運算符:
實例 | 等價 | 說明 |
---|---|---|
0...10 | 0 <= x <= 10 | 從0到10的閉區間 |
0..<10 | 0 <= x < 10 | 從0到10的左閉右開區間 |
-
數據存儲###
-
枚舉####
你可以聲明一個枚舉,像這樣:
enum ControlCMD {
case up, right, down, left
}
比如我們現在有個需求,滑動手指時輸出一個手指滑動的方向的英文,在Swift里我們只需要這樣實現:
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
類型的枚舉,這樣,我在發送消息時就可以使用cmd.rawValue
來訪問枚舉值的字符串了。
Swift中的枚舉還有一些更高級的用法,因為我暫時還沒有用過,所以也不在此描述了,以后遇上時再補充上。
-
元組####
對于元組這個概念,這是OC中所沒有的,我理解的元組是將多個值組合成為一個值,感覺有點像數組,但是元組中的值的類型可以是任意類型的,舉個例子:
let tuple = ("abc", 1, 0.5, [UIImage()])
這就是一個元組,元組中可以有多個元素,也可以只有一個元素,當然,由于元組就是為了存儲多個值的,如果只有一個值也就不需要元組,多個值的時候,我們可以像數組那樣,通過使用序號來訪問元組中的元素,比如這樣: