隨著WWDC2017大會的結束不出所料蘋果重磅推出Swift4.0.在4.0之前的語言其實是很讓開發人員頭疼的,首先版本差異大,底層結構變化,更可氣的是上下不兼容.不更新Xcode你不能對最新版本的手機系統開發,更新了,低版本的Swift語言報錯.所以了解一下Swift前后版本的語言差異對整個app的開發以及swift語言的認知是很有幫助的.那么既然這樣我們為什么要學那?因為Swift語言與大部分的開發語言形式上更加相似,對于iOS程序員在學習其他語言會提供很大的幫助,對于oc的語言我想大部分其他程序員看起來還是很頭疼的,還有Swift語言更加嚴謹,更加安全,而且蘋果公司最新文檔的更新早就不在用oc了,可見其推動Swift語言的決心而且很多公司也在做Swift的人才儲備。得了好處我就不多說了,感興趣的同學可以谷歌一下。而且可喜的是Xcode9兼容了Swift3.0與Swift4.0開發選擇,所以一下我只對Swift2.0與Swift3.0的語法總結做一下規整,希望對新學Swift的同學有一些幫助,在Swift2.0的總結中我會與oc的語言做一下簡單的對比,幫助oc開發人員加深理解:
Swift2.0 總結:
一.swift與oc的不同之處
1.與oc工程的一些不同點
(1).swift中沒有了 .h/.m文件 只有一個 .swift文件 聲明和實現都需要在同一個文件中
(2).在swift中 沒有非零既真的概念 只有true 和 false
(3). ( )表示初始化 也可以表示執行
(4).在swift中 默認所有的文件共享 所有的對象的方法也是默認可以調用
(5).命名空間: 在同一個項目下 叫做命名空間 在同一個命名空間下 所有的文件共享
(6).swift 屬性默認都是 強引用的
(7).swift中 init 方法 就是構造方法,并且所有的構造方法 都叫 init
2.類的聲明
oc的
@interface ViewController : UIViewController
swift的
class ViewController: UIViewController { }
3.可選項
可選項: ?(有值嗎) 1.有值 2.沒有值(nil)
必選項: ! 程序員承諾 一定有值 同樣也表示 強制解 包
每添加一個 '!'的時候都需要思考為什么,安全嗎 程序員承諾一定有值
經典報錯:fatal(致命的) error: unexpectedly(意外的,不希望) found nil while unwrapping(解包) an Optional(可選的) value
(1).可選項在打印的時候 會自動帶上 Optional(10)
(2).可選項不能夠直接參與運算
聲明可選項 在類型后面追加 '?"
var a: Int?
4.常量和變量
let: 常量 一經賦值 就不能被更改
var: 變量 可以被更改
盡量使用常量 等到需要修改的時候在改成 使用 var 來修飾
- 數據類型
(1).swift中 數據的類型是自動推導的 根據 '=' 右邊的類型來去推斷變量的具體類型
option + click 最常用的熱鍵值 沒有之一
swift 是一個類型極其苛刻的語言
(2).可以提前聲明類型: let 變量名: 類型
func demo7() {
let a=10
let b=12.4
//swift中 不支持 隱含形式的轉換類型,直接a+b會報錯哦
// let c = a+b
// print(c)
}
二.分支結構
swift中推薦使用的分支結構
1.switch case
(1).每個case 分支 必須要有一段可以執行的代碼
(2).臨時變量 的定義不需要加在 {} 中
(3).OC中只能判斷基本數據 swift中可以判斷任意類型
(4).可以同時case 多個選項
(5).不需要寫 break
func demo7() {
let m = "22200"
switch m {
case "18000", "22200":
print("高級工程師")
case "12000":
print("中級工程師")
case "8000":
print("初級工程師")
default:
print("你是猴子派來的嗎")
}
}
/*
1.條件語句 沒有 '()'
2.{} 不能省略
3.在swift中 沒有非零既真的概念 只有true 和 false
*/
func demo2() {
let a = 10
// if a > 5
// print("haha")
// print("hehe")
if a > 0 {
print("大于0")
} else {
print("小于0")
}
}
2.??操作符: 快速判斷可選項是否為 nil 如果為nil 給定默認值,在判斷字符串 或者基本數據類型的時候用處很大
例如:tableView 數據源方法 list?.count ?? 0 這里list代表一個數組對象如果為nil那么給一個默認值0;
三.解決可選項問題
推薦兩個方法 if let 和 guard let..else
if let 快速賦值 并且判斷是否為空 不為空 才能夠進入分支 意味著值就是一個必選項
func demo4() {
//url中包含中文 需要轉義
let urlString = " [http://www.douni.com?type](http://www.douni.com?type) "
if let url = NSURL(string: urlString) {
//當option + click string 時可以看到url是可選項
convenience init? 便利的構造函數 有可能能夠實例化一個對象 有可能無法實例化一個對象(nil)
let request = NSURLRequest(URL: url)
print(request)
}
}
guard(守衛) let ... else 和if let的 作用剛好相反
能夠減少一層分支嵌套
Xcode 7.0 swift2.0 才推出的語法
在項目使用的非常多 我自己也推薦這種寫法
func demo5() {
let urlString =" [http://www.douni.com?type=
中國](http://www.douni.com?type=中國) "
guard let url = NSURL(string: urlString) else{
//如果url為nil當前代碼 就直接返回
return
}
//程序運行到這里'守衛'的對象 就一定有值
let request = NSURLRequest(URL: url)
print(request)
}
四.循環
現代寫法 apple推薦的寫法
// 0..<10 0 ~ 9 表示范圍 不會向后包含
func demo1() {
for i in 0..<10 {
print(i)
}
}
// _ 表示忽略
func demo3() {
for _ in 0..<10 {
print("嘿嘿")
}
}
// 0...10 會向后包含 范圍 0 ~ 10
func demo2() {
for i in 0...10 {
print(i)
}
}
五.字符串
OC : NSString 類 繼承 NSObject
swift: String 結構體 更加高效,可以通過熱鍵查看
func demo1() {
let str: String = "葉良辰"
print(str)
//實例化一個字符串
var str1 = String()
str1 = "hello world"
print(str1)
}
//oc
-(void)demo1{
NSString * str = @"葉良辰";
NSLog(@"%@",str);
}
//字符串的一些基本特性
func demo2() {
//1.長度 一個中文 字符串的長度 是3 個字節
let str = "你若安好"
//獲取字節長度
let l = str.lengthOfBytesUsingEncoding(NSUTF8StringEncoding)
//獲取字符串的長度
// let l = str.characters.count
print(l)
//2.字符串的拼接
let str1 = "便是晴天"
// NSString stringWithFormat:"%@%@"
let str2 = str + str1
print(str2)
//字符串的判斷
//與oc方法的 isEqualToString方法一樣
if str == str1 {
print("======")
} else {
print("!=====")
}
//ASC 值進行比較
let s1 = "abc"
let s2 = "xyz"
if s1 > s2 {
print("大于")
} else {
print("小于")
}
//字符串的轉義
let i = 10
let str4 = "\(i)"
print(str4)
//字符格式化
// 1 23 45 拼接成 01:23:45
//format 格式化的類型 arguments: 可變參數
let h = 1
let m = 23
let s = 45
let str5 = String(format: "%02d:%02d:%02d", h,m,s)
print(str5)
}
我們可以看到相對于oc來講字符串的拼接比較等等得到了大大的簡化我們不需要再使用那長長的方法來進行比較
六.字典和數組
OC:NSArray [xxx]
swift: [元素]
let 修飾的數組 是不可變的數組
var 修飾的數組 是可變的數組
OC:字典 NSDictionary {}
swift: [key1 : value1, key2 : value2 ]
let 修飾的不可變的字典
var 修飾的可變的字典
func demo() {
//數組的定義 數組的類型也是可以自動推導
//數組中不推薦存放不同類型的元素 數組是通過索引來訪問元素
let array = ["張三","lisi","王五",16]
//實例化一個數組
var array1 = [String]()
//添加元素
array1.append("趙六")
array1.append("張三")
print(array1)
//改
//通過索引來去修改元素
array1[0] = "郭敬明天見"
array1[1] = "夏洛特煩惱"
//刪
// array1.removeFirst()
// array1.removeLast()
array1.removeAll()
//查
for str in array1 {
print(str)
}
//通過索引查詢數據
//fatal error: Array index out of range
// let str = array1[1]
// print(str)
}
//數組的拼接
func demo1() {
var array1 = [String]()
let arr1 = ["夏洛特煩惱","郭敬明天見"]
let arr2 = ["哈哈","呵呵"]
array1 += arr1
array1 += arr2
//拼接
let arr = arr1 + arr2
print(array1)
}
//字典的基本操作
func demo2() {
//聲明
let dict = ["name": "范冰冰","age" : 18,"title": "女神"]
//實例化字典類型
var dict1 = [String : NSObject]()
dict1["name"] = "張學友"
dict1["title"] = "歌神"
dict1["age"] = 18
// print(dict1)
//刪
dict1.removeValueForKey("age")
//改
dict1["name"] = "周杰倫"
//查 在OC中遍歷 字典 使用block
//()內鍵值的名字自己隨意取 但是一個對應的是 鍵(key) 第二個對應是值(value)
for (key, value) in dict1 {
print(key, value)
}
let name = dict1["name"]
print(name)
}
七.函數
swift中調用本類的函數和屬性 self可以省略 也可以加上 閉包內調用本類的函數和屬性時 必須加 self
override func viewDidLoad() {
super.viewDidLoad()
//函數的調用
//函數的第一個參數名稱可以省略
// sum(10, b: 20)
// let result = square(width: 10, height: 20)
// print(result)
demo3()
}
//函數沒有返回值的三種寫法
//1.沒有返回值的第一種寫法
func demo1() -> () {
print("哈哈哈")
}
//2.第二種寫法 Void V要大寫
func demo2() -> Void {
print("嘻嘻嘻")
}
//3.函數沒有返回值的第三種寫法
func demo3() {
print("黑hi額hi額")
}
//函數的外部參數
//外部參數 width是提供給調用方來進行使用的
//內部參數 a 是提供給函數內部使用的
func square(width a: Int,height b: Int) -> Int{
return a * b
}
//加法的函數
func sum(a: Int,b: Int) -> Int {
return a + b
}
析構函數
deinit {
//和OC dealloc 方法類似
print("VC 886")
}
八.閉包的基本使用
OC中block有哪些特點
1.可以定義為變量 block可以是一段提前準備好的可以執行代碼塊
let finishedCallBack = {
//就是一段可以執行的代碼塊
print("我就是回調執行的代碼塊")
}
2.可以當做函數的參數傳遞
loadData1("sisi", finished: finishedCallBack)
3.在需要執行的時候執行 可以產生回調的效果 (協議/代理,通知) block 可以有返回值 但是用的不多
(1).() -> () 閉包的最基本的類型 沒有參數 沒有返回值的閉包
(2).系統將閉包進行了簡寫
(3).必須是閉包參數是函數的最后一個參數 才可以進行閉包的簡寫 -> 將可執行的閉包挪動到參數體外面 ,形成 '尾'隨閉包
最多使用的還是這一種方法
loadData("sisi") { (dataString) -> () in
//在閉包中智能提示 不是很好使 通常 需要生寫
print(dataString)
}
如果有閉包參數 建議 將閉包參數寫在最后一個 可以寫成尾隨閉包
demo({ () -> () in
print("回調")
}, userId: "sisi")
}
func loadData(userId: String, finished: (dataString: String) -> ()) {
//耗時操作
dispatch_async(dispatch_get_global_queue(0, 0)) {
//在全局隊列中執行耗時操作
print("開始加載數據")
//將結果回調到主線程
dispatch_async(dispatch_get_main_queue(), {
//執行回調
// print("===========")
//執行函數 其實閉包 知識一個匿名函數
// {
// print("回調的結果")
// }()
finished(dataString: "辦證: 138XXXXXXX")
print("+++++++++++")
})
}
}
.....................................................................................
loadData1("思思") { () -> () in
print("回調的結果")
}
//返璞歸真
loadData1("思思", finished: { print("回調結果") })
//比較殘暴一種寫法
loadData1("思思") {
print("回調的結果")
}
func loadData1(userId: String, finished: () -> ()) {
//耗時操作
dispatch_async(dispatch_get_global_queue(0, 0)) {
//在全局隊列中執行耗時操作
print("開始加載數據")
//將結果回調到主線程
dispatch_async(dispatch_get_main_queue(), {
//執行回調
// print("===========")
//執行函數 其實閉包 知識一個匿名函數
// (): 標示初始化 也可以標示執行
// {
// print("回調的結果")
// }()
finished()
print("+++++++++++")
})
}
}
4.在block中 使用self 需要考慮 循環引用
函數只是 一種特殊閉包 在swift 中閉包才是老大(第一公民)
九.閉包的循環引用
解決方式有兩種[unowned self]和[weak self]但是[unowned self]當有網絡延時的時候會崩
十.懶加載
懶加載 什么時候調用 什么時候在初始化
在swift 懶加載有特殊的寫法
就是一個可以被執行閉包這樣可以在里面進行一些設置
lazy var nameLabel: UILabel = { () -> UILabel in
print("我懶了嗎")
let l = UILabel()
return l
}()
簡單寫法
lazy var ageLabel: UILabel = UILabel()
十一 單例
一般在音頻,文件,網絡請求,管理類都適合用單例
由于單例這較為特殊那我就貼出代碼大家比較一下:
第一種 : oc的方法
在.h文件中實現
在.m文件中實現
在橋接文件里導入頭文件
去文件里使用單例
第二種Swift方法
一般我們用下邊這個方法就好,一句話搞定(swift中單例對象很簡單 最主要需要 依賴 let 一經賦值就不可以被更改)
//簡單寫法
static let sharedTools: SoundTools = SoundTools()
十二.面向對象必選屬性的構造
自定義對象一般作為一個模型保存數據 創建對象的類繼承NSobject
如: let p=person()
我們創建一個name屬性 var name: String 因為這樣所以name 是一個必選屬性,系統會報錯,解決方式有兩種
- 第一種
var name: String = ""
- 第二種
初始值為nil 沒有分配內存空間 延遲分配控件 什么時候用 什么分配內存控件 這種方式 是后面會大量運用到
var name: String?
3.第三種 構造方法解決,比較麻煩
override: 重寫 覆蓋, 父類已經存在這個函數 子類需要在父類的這個函數進行其他的一些操作
var name: String
//person的構造方法
override init() {
self.name = "李冰冰"
super.init()
//description 是NSobject的屬性所以super.init之后
self.description
// self.name = "李冰冰"他是子類或本類的屬性所以在super.init之前
}
let s =Student()
在創建一個子類對象的時候就會調用本身的init構造方法,而init構造方法中有[super init]就會往下調用父類的init構造方法一次類推形成遍歷直到NSObject調用了init方法,對象才算構造完成,就可以使用"s"對象了這時對象"s"中就會擁有本類和父類的一切屬性或方法
十三.函數的重載
我們可以看到在上述的構造方法中我們很容易把對象的屬性寫死導致參數不靈活所以可以利用函數的重載
函數的重載: 函數名相同 函數的參數的類型或者參數的個數不同 就形成了函數的重載
重載不僅僅發生在構造函數中
注意: 不能夠和 重寫混合記憶
重載有啥好處呢?
1.可以簡化程序員記憶 wash(member:洗衣粉)
2.重載可以簡化編譯 操作符 減少大量使用不同的操作符 可以提升編譯的效率
重載是面向對象的重要標記之一
class Person: NSObject {
//初始值都是 nil 等到super.init結束之后 還是沒有值
var name: String
//age 整數類型也是nil oc整數的默認值是 0
//this class is not key value coding-compliant for the key age.'
//但是Int 在OC中是基本數據類型 不會主動調用 '初始化'方法 ,直接設置初始值之后,就會分配內存空間 一般基本數據類型都設初始值
var age: Int = 0
override init() {
self.name = "李冰冰"
self.age = 18
super.init()
}
//swift所有的構造函數 都是 init
/*
構造函數 有責任也有義務 確保 必選屬性必須有值
*/
//函數的重載名字一樣,參數類型或個屬不同
init(name: String, age: Int) {
self.name = name
self.age = age
super.init()
}
十四 字典轉模型 kvc (這是基本接近于底層的字典轉模型了,但是他還是依據的oc的運行時原理Swift3.0,4.0不知底層是否發生變化,望有大神給予指點)
//字典轉模型 使用KVC設置初始值
init(dict: [String : NSObject]) {
//kvc設置初始值 在運行時 間接給 '對象' 發送 setValue: forKey:
super.init()
//遍歷字典中的鍵值對 給對象轉發 setValue: forKey:
setValuesForKeysWithDictionary(dict)
}
//會檢查 字典中的鍵值對應的屬性的key 是否存在 如果存在就只直接賦值 不存在就會將消息轉發給 setValue: forUndefinedKey:
override func setValue(value: AnyObject?, forKey key: String) {
super.setValue(value, forKey: key)
}
//如果沒有重寫 默認會調用父類的這個方法
//就是起過濾作用
override func setValue(value: AnyObject?, forUndefinedKey key: String) {
print(value,key)
// super.setValue(value, forUndefinedKey: key)
}
十五.便利的構造函數
override init() {
self.name = "李冰冰"
self.age = 18
super.init()
}
- 可以返回nil
2.必須使用 '本類' 的構造函數來進行實例化 需要使用 'self' 來調用構造函數
3.遍歷的構造函數本質上 是基于本類 '指定' 的構造函數(必須實例化對象) 來進行快速的擴展
convenience init?(name: String, age: Int) {
//通常 年齡需要做合法性的檢查 讓一個人年齡在一個合法區間內
if age < 0 || age > 1000 {
//不能夠實例化該對象 必須是失敗的構造器才能夠返回 nil
return nil
}
//保證對象在 滿足條件下 能夠被實例化
//生寫 指定構造函數
self.init(dict: ["name" : name, "age" : age])
}
//字典轉模型 使用KVC設置初始值
init(dict: [String : NSObject]) {
super.init()
setValuesForKeysWithDictionary(dict)
}
十六.getter setter
//使用 didSet 重寫set方法
var name: String? {
//值設置完畢之后會調用到didSet
didSet {
//通常在swift開中 使用這個方法來替代重寫 set方法
//一般這里面操作 模型數據的綁定
print(name)
}
}
//只讀屬性 readonly
//在swift 中又特殊的叫法: 計算型屬性
//必須依賴其他的屬性進行計算,每次執行都會調用
var title: String? {
get {
//只讀屬性是沒有存儲空間
return "Mr" + (name ?? "")
}
}
//是否成年
// var isAdult: Bool {
// get {
// return age > 18
// }
// }
//計算型屬性可以簡寫 可以將 getter省略掉
var isAdult: Bool {
return age > 18
}
好吧swift2.0整理到此結束,好累。。。下面Swift3.0(這里我總結的代碼就以截圖形式呈現吧,因為通過Swift2.0的總結我發現對于我們程序員來說在閱讀注釋方面的還是喜歡Xcode下的格式更舒服一些)
Swift3.0總結
- Swift 中取消了預編譯指令包括宏
- Swif取消了objective-c 的指針及其他不安全訪問的使用
- 舍棄早起應用Smalltalk語法,全面改為點語法
- 3.0對Foundation框架做了重大調整 ,去除了NS前綴 ,將絕大部分class(類)轉換為struct(結構體)
- switch支持任意類型的數據以及各種比較操作,不僅僅是整數以及測試相等。
let vegetable = "red pepper"
switch vegetable {
case "celery":
print("Add some raisins and make ants on a log.")
case "cucumber", "watercress":
print("That would make a good tea sandwich.")
case let x where x.hasSuffix("pepper"):
print("Is it a spicy \(x)?")
default:
print("Everything tastes good in soup.")
}
運行switch中匹配到的子句之后,程序會退出switch語句,并不會繼續向下運行,所以不需要在每個子句結尾寫break。
6.你可以使用for-in來遍歷字典,需要兩個變量來表示每個鍵值對。字典是一個無序的集合,所以他們的鍵和值以任意順序迭代結束。
let interestingNumbers = [
"Prime": [2, 3, 5, 7, 11, 13],
"Fibonacci": [1, 1, 2, 3, 5, 8],
"Square": [1, 4, 9, 16, 25],
]
var largest = 0
for (kind, numbers) in interestingNumbers {
for number in numbers {
if number > largest {
largest = number
}
}
}
print(largest)
二,變量與常量
三,可選項
四,switch的用法
五,字符串的長度和遍歷
六,通過閉包回調傳遞參數
七,循環引用
八,必選屬性的構造過程
九,可選項思維導圖
Swift4.0
暫不更新原理基本和2.0 ,3.0大同小異大家可以用新版的Xcode進行書寫來發現其中的異同點,這樣對Swift的理解是非常重要的,有時大家看10篇文章不如寫上一段代碼理解的透徹現附上WWDC相關視屏及資料
http://mp.weixin.qq.com/s/DTl9INDMrkivciJwJSWd5A