特色
(1)優於OC,快速,安全
(2)取消了預編譯指令包括宏定義(OC用的太多了)
(3)取消了OC指針和不安全訪問的使用(看不到星星了)
(4)捨棄 Objective-C 早期應用 Smalltalk 的語法,全面改成點語法
(5)3.0中對Foundation框架作了不少改變,去除了NS,將絕大部分class轉換成struct結構體(爲了考慮性能和安全性,絕大部分使用結構體來代替之前的類,可是在實際使用感受不到)
(6)可使用現有的 Cocoa 和 Cocoa Touch 框架
, 之前是OC調UIKit,如今就是Swift調UIKit,這點事沒問題的
(7)Swift由於語法的嚴謹性可讓不少錯誤提早遇到,這樣不多出現bug讓程序停在main致使沒法找到
(8)@UIApplicationMain是程序的入口
(9)只有.h沒有.m
(10)全部的代碼都包括在{}裏,默認方法func都有縮進!
(11)語法的allocinit替換成()java
PlayGround
(1)能夠看蘋果官方自帶的tips和100個tips,都在Placground來使用ios
基礎點
(1)不適用self. 在閉包或者編譯器提示的時候再使用
(2)分號是用來分割語句的,若是一行洗不少,就能夠加分號,通常時候能夠不加
(3)#function打印執行的函數
(4)添加標記用到// MARK: - 選擇,若是是接下來要作的能夠用// TODO:和// FIXME:這些很是有用程序員
var 定義變量,賦值以後仍然能夠修改
常量和變量的細節json
// 定義常量而且直接設置數值 let x: Int = 10 // 常量數值一經設置,不能修改,如下代碼會報錯 // x = 30 let y: Int // 常量有一次設置的機會,如下代碼沒有問題,由於 `y` 尚未被設置數值 y = 10 // 一旦設置以後,則不能再次修改,如下代碼會報錯,由於 `y` 已經被設置了數值 // y = 50 print(x + y) // 變量設置數值以後,能夠繼續修改數值 var z: Int z = 100 z = 200 print(x + y + z)
Swift 可以根據右邊的代碼,推導出變量的準確類型
只有相同類型的值纔可以進行運算swift
// 整數默認的類型是 Int let intValue = 200 // 小數的默認類型是 Double let doubleValue = 10.5 // 若是要對不一樣類型的數據進行計算,必需要顯式的轉換 print(intValue + Int(doubleValue)) print(Double(intValue) + doubleValue)
注意:Swift對類型要求異常嚴格,任何不一樣類型的數據不能直接運算(哪怕是Int和Double),不會作一些自動的轉換來轉換成Double。Swift不存在基本數據類型,Int和Double都是結構體其實,強轉用Double(x)完成,或者在定義的時候直接指定變量的類型let x : Double = 10;(不多使用)api
let num = 100 if num > 10 { print("大 \(num)") } else { print("小 \(num)") }
num > 10 ? print("大 \(num)") : print("小 \(num)")
或者數組
num > 10 ? print("大 \(num)") : () 這樣就對後面的不做處理。 () 表示空執行。
let scoreString = "優" switch scoreString { case "優": let name = "學生" print(name + "80~100分") case "良", "中": print("60~80分") case "差": print("不及格") default: break }
switch 的條件判斷安全
let score = 90 switch score { case _ where score >= 80: print("優") case _ where score >= 70: print("良") case _ where score >= 60: print("中") default: print("差") }
for i in 0...5 { } for i in 0..<5 { }
for i in (0..<10).reversed() { }
(1)定義變量時,若是是可選的,表示能夠有值,也能夠是nil,用「?」
(2)強行解包 「!」,程序員來注意!,而且要少用,可能會崩
(3)最多見的錯誤:解包的時候發現nil。fatal error: unexpectedly found nil while unwrapping an Optional value
(4)let可選的話,沒有默認值,須要賦值。var可選的話,默認值爲nil
(5)可選項在參與計算時候必須解包閉包
// 格式1 let x: Optional = 20 // 格式2 let y: Optional<Int> = 30 // 格式3 let z: Int? = 10 print(x) print(y) print(z)
var x1: Int? print(x1) let x2: Int? // 常量可選項沒有默認值,在賦值以前不能使用 // print(x2) x2 = 100 print(x2)
print(x! + y! + z!)
程序員要對每個 ! 負責app
var optionValue: Int? print(optionValue?.description) // 輸出 nil optionValue = 10 print(optionValue?.description) // 輸出 Optional("10")
與強行解包對比,可選解包更安全,可是隻能用於函數調用,而不能用於計算
因爲可選項的值可能爲 nil,不容許直接參與運算,所以在實際開發中,常常須要判斷可選項是否有值。
若是單純使用 if,會讓代碼嵌套層次很深,不宜閱讀和維護,爲了解決這一問題,蘋果提供瞭如下三種方式:
注意:?? 的優先級低,在使用時,應該注意使用 ()
let x: Int? = 10 let y: Int? = 100 print((x ?? 0) + (y ?? 0))
let name: String? = "Mike" let age: Int? = 18 if let name = name, let age = age { print("\(name) 今年 \(age) 歲") } else { print("姓名或者年齡爲 nil") }
func demo(name: String?, age: Int?) { guard let name = name, let age = age else { print("姓名或者年齡爲 nil") return } print("\(name) 今年 \(age) 歲") } demo(name: name, age: age)
由於總會取名字,if let name = name這樣就能夠,注意後面使用的時候用非空的那個!而且iflet和guardlet能夠依次判斷,先判斷是一個字典,再拿字典的數組,在判斷數組的值,能夠一條線判斷出來。
用String,是一個結構體,具備絕大多數NSString功能,支持直接遍歷
(1)遍歷:
func demo3() { // 字符串遍歷(NSString不支持這麼遍歷) let str = "wowosnshi是" for s in str.characters { print(s) } }
(2)長度:
// 返回指定編碼對應的字節數,每一個漢字三個字節 print(str.lengthOfBytes(using: .utf8)) // 返回真正字符串長度 print(str.characters.count)
(3)拼接:要注意可選項拼接不解決會帶上Optional,剩下的均可以拼接,不再用看StringWithFormat了
let name = "AA" let age = 19 let title : String? = "sss" print("\(name)\(age)\(title ?? "")")
(4)格式化:
let h = 8 , m = 10, s = 44 // OC中用stringWithFormat格式化日期,Swift中能夠 let strDate = String(format: "%02d-%02d-%02d", h,m,s) print(strDate)
(5)截取字符串:建議用NSStrin做中轉,由於swift取串方法一直在改變
let str = "紅紅火火恍恍惚惚" let strOC = str as NSString strOC .substring(to: 1) strOC.substring(with: NSMakeRange(0, 2))
(1)就是中括號,注意數組的類型,而且基本數據類型不須要包裝,能夠直接方數組裏,若是類型不同(混合數組,可是基本不用),自動推導[NSObject]。在Swift中還有一個[AnyObject類型],標示任意對象,由於在Swift中一個類能夠沒有任何父類。
(2)遍歷:
// 遍歷1(按照下標遍歷) for i in 0..<array.count { } // 遍歷2(遍歷元素) for s in array { } // 遍歷3(同時遍歷下標和元素) for e in array.enumerated() { // let e: (offset: Int, element: String) e是一個元組 print("\(e.offset), \(e.element)") } // 遍歷4(同時遍歷下標和元素) for (n,s) in array.enumerated() { print("\(n),\(s)") } // 反序遍歷 for s in array.reversed() { } // 反序索引下標(這樣寫纔對,先枚舉再反序) for (n,s) in array.enumerated().reversed() { }
(3)增刪改:
array.append("AA") array[1] = "BBB" array.remove(at: 2)
(4)合併:用「+」號。可是要合併的數組的兩個類型必須一致。
通常是[String:NSObject],對應鍵值對.因爲3.0後大部分都是結構體了,AnyObject很差用了,Any範圍更大
(1)字典數組:
(2)增刪改:和數組都相似,就是兩個字典合併不像數組直接相加,而是須要遍歷
(1)外部參數,當外部參數用_替代的時候,會在外部調用的時候忽略形參名
(2)函數的默認值(OC不具有),這個使Swift比OC靈活不少不少,一個方法能夠作不少事,由於OC會有各類參數和組合,Swift只需寫一個最多的參數,而後不須要的設定默認值就是了
(3)無返回值 :直接省略 () Void均可以
(4)閉包:相似Block,比Block還普遍。OC中Block是匿名函數,Swift中函數是特殊的閉包。閉包在整個開發中和Block的應用場景同樣。用於控制器/自定義視圖/異步執行完成的回調。這些回調的特色就是都是以參數回調處理結果,返回值爲Void。
let biBao = { (x: Int) -> Int in return x + 100 } print(biBao(10))
func loadData(compeletion:@escaping ( _ result: [String])->()) -> Void { DispatchQueue.global().async { print("耗時操做會得到一些結果 \(Thread.current)") Thread.sleep(forTimeInterval: 1.0) let json = ["天氣","不錯","颳大風"] // 主線程回調 DispatchQueue.main.async(execute: { print("主線程更新UI \(Thread.current)") // 回調 -> 經過參數傳遞 執行閉包 compeletion(json) }) } }
調用:
// 執行的適合我就拿到了值 loadData { (result) in print("獲取的新聞數據 \(result)") }
尾隨閉包:若是函數的最後一個參數是閉包,那麼參數就省略了,最後一個參數直接{}大括號包裝
閉包的循環引用:
(5)面向對象(各類構造函數):()就是allocInit,在Swift中對應init()。在swift中一個項目全部類都是共享的,能夠直接訪問,每個類都默認有一個命名空間。A.name B.name God.name Dog.name。同一個類能夠從屬於不一樣的命名空間(假若有一個框架有Person類,作用戶,還有一個框架作後臺,也用Person。在OC中就只能靠前綴解決,HouTaiPerson,KuangJiaPerson。而Swift中的命名空間就是項目名。AAA項目有一個Person,那麼AAA.Person就是AAA的Person類,此時再導入框架,那也是框架的.Person)
思路:OC是先調用爸爸。就是Person,Person會再調用NSObject,就是先跑上去什麼都無論,先初始化了NSObject,而後才往下走挨個初始化。Swift是把本身徹底初始化,再上去初始化爸爸,這麼看比OC快了一圈,性能要好。
注意:若是重載了構造函數而且沒有實現父類的init,那麼系統再也不提供init構造函數了(默認是有的),由於默認的構造函數不能給本類的屬性分配空間(你不本身寫name = ,系統就沒辦法分配)
KVC構造函數:只需記住下面4點
因此通常在模型中加個? 而後用KVC實現(先調用init由於是運行時機制)
總體
(6)便利構造函數:關鍵字Convenience(開發用的不多,由於模型都是框架轉,UI不須要便利)
*目的:條件判斷,只有知足條件才實例化對象,防止沒必要要的內存開銷,簡化對象建立。自己是不負責屬性的建立和初始化的。
(7)deinit:相似OC的Dealloc
便利構造函數 + 分類能夠省略抽取不少代碼。例如給UITextField/UIButton寫分類,而後寫便利構造函數,方便。
在OC開發中,懶加載通常自定義控件。在Swift中,懶加載仍是須要用的,能夠保證控件延遲建立,還能避免處理控件解包。若是直接定義控件var label = UILabel,根據代碼從上到下,會讓控件在ViewDidLad以前就提早建立了。因此須要懶加載。OC中懶加載就是Get方法,Swift直接lazy var。固然也能夠private lazy var來限定做用域。
(1)簡單的懶加載:
(2)完整的懶加載:()就是函數執行,就是一個特殊的閉包,因此懶加載本質是一個閉包。通常不這麼寫。
(3)OC和Swift區別
- (UILabel *)label{ //若是label等於nil就會建立! if (_label == nil) { _label = [[UILabel alloc]init]; _label.text = @"loirou"; [_label sizeToFit]; _label.center = self.view.center; } return _label; }
OC是等於nil時候就懶加載
[self.view addSubview:self.label]; //釋放label _label = nil; //會再次調用懶加載的代碼 NSLog(@"%@",self.label);
當label設nil的時候就在此調用。在ios6中,didReceiveMemoryWarning是不清理視圖的。
此時釋放的時候就會報錯。由於定義的時候沒有?,就是必定有值得。
那麼若是定義時候加? 一旦label = nil,也不會在執行懶加載了!由於懶加載根本沒寫若是是空就建立。
懶加載只會在第一次調用的時候執行閉包。Swift中必定注意不要主動清理視圖或控件,由於懶加載不會建立了(例如內存警告別幹掉控件,幹掉了在也用不成了,由於懶加載就一次)
(1)getter/setter(開發不用):
// 開發通常不用,還給搞一個_name。 // swift通常不會重寫getter和setter private var _name: String? // 僞裝的一個值 var name: String? { get{return _name} set{_name = newValue}} // get返回成員變量 set記錄成員變量 override func viewDidLoad() { super.viewDidLoad() demo() } }
(2)計算型屬性:readOnly只讀:OC中重寫get。Swift也是重寫get,無論set就能夠。
// 只讀,此時set賦值時候就會報錯 var name: String? { get{return "ABC"}} // 還有一種簡寫: var name: String? { return "ABC"}
看這類屬性,是自己不保存內容的,都是經過計算得到結果。就能夠當我就是個沒有參數只有返回值的函數!!我每次return值給你個人任務就完成了。每次你調用我這個屬性的時候,我都會進行一次計算!都會執行個人代碼而後return給你。我自身不存儲的。
(3)懶加載和計算型屬性的區別:
(4)存儲型屬性:須要開闢空間,存儲數據。通常的屬性都是存儲型屬性(懶加載)
(5)存儲和計算均可以?或者不加。看狀況是否是必選
(四)Swift中設置模型數據:
Swift作好模型後。別的控件拿到模型後,由視圖本身來顯示。此時在didSet裏寫。就是替代OC的Setter方法。(OC的Setter要考慮_成員變量 = 值,並且若是是copy須要.copy,而Swift不須要考慮一切)
賦值
// 獲取命名空間的值,可選 let str = Bundle.main.infoDictionary?["CFBundleName"] as? String ?? "" let con = NSClassFromString(str + "." + "ViewController") as? UIViewController.Type
對於任何類均可以知道類的全部屬性和方法,對於任何對象均可以調用任何屬性和方法,這種動態獲取的信息和動態調用對象屬性方法的功能成java的反射機制(Swift也有了)
(1)在OC中利用反射機制
(2)在Swift中利用反射機制相似。工做中用的不少不少。
場景:AppDelegate(OC中也用過,利用NSClassFromString得到類,而後設置根控制器。可是Swift中多了一個命名空間寫法。)
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { window = UIWindow(frame: UIScreen.main.bounds) // 依據String名字拿到控制器(添加項目名稱,命名空間,不能有數字和特殊符號) // 返回的是AnyClass? 須要as?強轉 // 控制器添加Type類型 let rootControl = NSClassFromString("SwiftyDemo.ViewController") as? UIViewController.Type let vc = rootControl?.init() window?.rootViewController = vc window?.makeKeyAndVisible() return true }
(3)第三方框架,用了不少反射機制和工廠方法,爲了實現大量的解耦和封裝,很麻煩。一個方法可能跳10個方法10個控制器才寫了一個加法。可是若是涉及高級開發和封裝,必需要通過這一步。