Swift與OC的不同點
- 導入框架的方式
- OC使用#import <UIKit/UIKit.h>
- Swift使用import UIKit
- 定義標識符的方式
- Swift中定義標識符,必須指定該標識符是一個常量還是一個變量
- 語句結束后的標志
- Swift可以不用分號";"分割(只限于一行有一條語句時)
- OC需要分號進行分割
- 打印語句
- 直接使用print()語句進行打印
- OC中使用NSLog()語句進行打印
常量和變量的使用注意
- 優先使用常量
- 常量的本質:保存的時常量的內存地址,不可以修改,但可以通過內存地址拿到對象內部屬性進行修改
Swift中的類型推導
在定義一個標識符,如果有直接賦值,那么會根據賦值的類型推導出前面標識符的類型,所以后面的類型可以省略
Swift中沒有隱式轉換,所以只有相同類型之間才可以進行運算
顯式類型轉換:Int(),Double()等
邏輯分支
if條件判斷可以不要括號,沒有非0即真的概念
- OC中BOOL的值為YES/NO
- Swift中的布爾Bool類型的值為true/false
guard的使用,適合多層if嵌套,使用guard代替,該語法是自Swift 2.0以后推出的.作用和if相同,但是可讀性比if好.
switch的用法:
- switch可以不跟()
- case語句結束后,不跟break,系統會默認添加
- 如果希望一個case中出現case穿透,可以在case語句結束后跟上fallthrough
- case后面可以跟多個條件,多個條件以","分割
- switch可以判斷浮點型
- switch可以判斷字符串
- switch可以判斷區間
swift中的區間表示:
- a..<b 相當于數學上 [a, b)
- a...b 相當于數學上 [a, b]
- swift中沒有全開區間的概念,即沒有(a, b)的寫法
swift中的for循環:
- for后面的括號可以省略
- swift3中已經移除了普通的for循環寫法
- forin寫法:區間遍歷
- forin寫法,如果不需要用到下標值,可以用下劃線"_"省略
swift中的while循環
- 在OC中,使用do while循環
- 在swift中,使用repeat while循環
字符串
swift中的字符串為String類型,是個結構體.而OC中的NSString是對象,所以swift中的字符串類型比OC的效率高.
- 定義字符串,可以不用指定String類型
- 可以直接遍歷字符串中的字符,使用for c in str.characters{}
- 字符串的拼接
- 直接使用str1+str2
- 拼接其他標識符的字符串:使用(str)
- 字符串的格式化:String(format:"%02d:%02d", arguments: [min, second])
- 字符串的截取:可以先轉換為OC的NSString類型,再進行截取
- (urlString as NSString).substring(to: 3)
- (urlString as NSString).substring(with: NSRange(location: 4, length: 5))
- (urlString as NSString).substring(from: 10)
數組
- 數組的定義:
- 可變數組(使用let聲明):let array = ["a", "b"]
- 不可變數組(使用var聲明):
- var arrayM = Array()
- var arrayM = [String]()
- 對可變數組的操作:
- 添加元素:arrayM.append("c")
- 刪除元素:arrayM.remove(at: 0)
- 修改元素:arrayM[0] = "m"
- 獲取元素:arrayM[0]
- 遍歷數組:
- for i in 0..<arrayM.count {}
- for name in arrayM {}
- for i in arrayM[0..<2] {}
- 數組的合并:
- swift中,如果兩個數組類型是完全一致的,才可以進行相加合并.
字典
- 字典的定義:
- 不可變字典(let)
- 注意:在swift中無論是數組還是字典,都使用[],但是如果[]中存放的是元素,編譯器會認為是一個數組.如果[]中存放的是鍵值對,編譯器會認為是一個字典.
- let dict = ["key" : "value"]
- 可變字典(var)
- var dictM = Dictionary<String, AnyObject>()
- var dictM = [String : AnyObject]()
- 注意:一般在指定數據類型時,使用AnyObject,在聲明變量類型時使用NSObject.
- 對可變字典的操作:
- 添加元素:dictM["newKey"] = "newValue"
- 修改元素:dictM["key"] = "newValue",如果存在key,則覆蓋該key之前的value,如果不存在key,則直接添加.
- 刪除元素:dictM.removeValue(forKey: "key")
- 獲取元素:dictM["key"]
- 遍歷字典:
- 遍歷所有的key:for key in dictM.keys {}
- 遍歷所有的value:for value in dictM.values {}
- 遍歷所有的key/value: for (key, value) in dictM {}
- 合并字典:
- 注意:即使兩個字典的類型一致,也不能進行相加合并
- 如果必須進行合并,可以遍歷一個字典,將其逐個添加到另外一個字典中.
元組
元組一般用于函數的返回值,其與數組和字典功能類似,但是寫法更為直觀,所以swift中新增了元祖的類型.
- 元組的基本寫法:let userInfo = ("name", 18, 1.8)
- userInfo.0
- 給每一個元素起別名:let userInfo = (name: "name", age: 18, height: 1.8)
- userInfo.name
- 別名就是元組變量的名稱
- let (name, age, height) = ("name", 18, 1.8)
- 直接使用name,age,height即可獲取元組中元素的值
可選類型
可選類型的作用:當聲明一個變量為String類型,而想要給它賦值為nil,需要使用可選類型.
- 定義方式:
- 方式一:基本定義方式,不常用,var name: Optional = nil
- 方式二:語法糖,常用,var name: String? = nil
- 給可選類型賦值:name = "abc"
- 在使用可選類型時,拿到optional(具體的值)
- 強制解包:
- 通過在變量后使用"!"來進行強制解包
- 注意:強制解包是非常危險的過程,在強制解包時,如果里面沒有值,程序會發生崩潰.
- 嚴謹的解包方式:if name != nil {}
- 可選綁定:
- 如果name不等于nil,則解包name,并且將解包后的值賦值給新值
- 方式一(不常用):if let tempName = name {}
- 方式二(常用):if let name = name {}
- 以上操作其實可以分為兩步:
- 首先判斷name是否有值
- 如果有值,則進行強制解包(系統會默認處理),并且將解包后的值賦給前面的常量,并執行大括號里面的代碼
- 如果沒有值,則不進行強制解包,同時也不執行括號里面的代碼
可選類型的應用場景
在使用一個字符串創建URL類型的常量時,需要使用可選類型
因為如果字符串中包含中文時,創建的URL對象可能為空
在使用的時候,需要對可選類型進行判斷,此時可以使用可選綁定,在大括號內創建NSURLRequest對象
函數
在OC中叫做方法,在swift中叫做函數
- 沒有參數,沒有返回值
- func about() -> Void {}
- func about() {}
- 沒有參數,有返回值
- func readMessage() -> String {}
- 有參數,沒有返回值
- func callPhone(phoneNum : String) -> Void {}
- func callPhone(phoneNum : String) {}
- 有參數,有返回值
- func sum(num1: Int, num2: Int) -> Int {}
函數的注意點
- 注意一:內部參數和外部參數
- 內部參數:在函數內部可以看到的參數就是內部參數,默認情況下所有的參數都是內部參數
- 外部參數:在函數外部可以看到的參數名稱就是外部參數,在swift3之前默認從第二個參數開始既是內部參數又是外部參數,但是從swift3開始,默認第一個內部參數也是外部參數.
- 注意二:swift中的默認參數
- func makeCoffe(coffeName: String = "雀巢") -> String {}
- 在調用函數是,如果沒有傳遞某個參數的值,swift會根據函數的聲明,自動添加默認參數的值.
- 注意三:可變參數
- func sum(num: Int...) -> Int {}
- 注意四:指針類型
- swift函數參數,默認是值傳遞,在函數中直接交換兩個參數的值,調用函數后,參數的值并不會發生改變.
- func swapNum(m: inout Int, n: intout Int) {}
- 調用時:swapNum(m: &m, n: &n)
- 注意五:函數的嵌套使用(用的不多,了解即可)
- 在函數內部,可以聲明新的函數,如果不調用,則不會執行函數內的代碼.
- 只有在調用了函數,才會執行,哪里調用就在哪里執行.
Swift中類的注意事項
- 類的定義
- 可以不繼承任何類:class Person {}
- 可以繼承NSObject:class Person : NSObject {}
- 不繼承父類,可以使自定義的類更加輕量級.如果需要使用到NSObject中的屬性或方法,則需要繼承,比如使用KVC對屬性進行賦值
- 注意:任何一個類,都要保證它里面的屬性,在類初始化的時候要有一個默認值.
- 創建類對應的對象
- let p = Person()
- let p: Person = Person()
- 給類的屬性賦值
- 可以通過點語法直接賦值
- 也可以通過KVC,調用類對象的setValuesForKeys()方法
- 可以重寫setValue...forUndefinedKey方法,那么字典中沒有的字段可以在類中沒有對應的屬性,程序也不會報錯.
- 如果要寫的某一個方法是對父類方法的重寫,那么必須在該方法前加上override關鍵字
定義Swift類中的屬性
在swift中,聲明的類屬性都需要進行初始化,類屬性分為三類:
- 存儲屬性:
- var age: Int = 0
- var mathScore: Double = 0.0
- var chineseScore: Double = 0.0
- 計算屬性:
- 通過別的方式計算到結果的屬性,稱之為計算屬性
- var averageScore: Double { return (mathScore + chineseScore) * 0.5 }
- 類屬性:
- 類屬性是和整個類相關的屬性,而且是通過類名進行訪問
- 在生成單例的時候,會用到類屬性
- static var courseCount: Int = 0
給類屬性賦值:Student.courseCount = 2
創建對象:let stu = Student()
給對象的屬性賦值:stu.age = 10
直接使用計算屬性:let averageScore = stu.averageStudent
在swift開發中,如果使用當前對象的某一個屬性,或者調用當前對象的某一個方法時,可以直接使用,不需要加self.但是在產生歧義的情況下,還是需要加上self的.
Swift中類的構造方法
swift中類的構造函數和OC中的初始化方法功能相同,只是寫法不同.
在構造函數中,如果沒有明確super.init(),那么系統會幫助調用super.init()
- 自定義構造函數:
- 參數非字典類型:init(name: String, age: Int) {}
- 參數為字典類型:
- 可以直接遍歷字典,在遍歷字典的時候,需要判斷可選類型,然后轉換可選類型,再使用可選綁定,對屬性進行賦值
- 也可以直接使用KVC,但是在調用setValueForKeys()前,需要調用父類的初始化函數super.init(),雖然系統會默認調用該函數,但是在自定義構造函數尾部才去調用,所以需要首先調用super.init().
- 在使用KVC的時候,也需要重寫setValue(forUndefinedKey)方法,以便處理類中沒有定義key的屬性.
屬性監聽器
class Person: NSObject {// 屬性監聽器var name : String? {// 屬性即將改變時進行監聽willSet {print(name)print(newValue)}// 屬性已經改變時進行監聽didSet {print(name)print(oldValue)}}
}
回顧OC中的block
使用block模擬網絡請求中的回調
- 自定義一個網絡請求的工具類HttpTool,提供loadData方法
- (void)loadData:(void (^)(NSString *))callBack
{dispatch_async(dispatch_get_global_queue(0, 0), ^{NSLog(@"發送網絡請求:%@", [NSThread currentThread]);dispatch_sync(dispatch_get_main_queue(), ^{NSLog(@"拿到數據,并且進行回調:%@", [NSThread currentThread]);callBack(@"json數據");});});
}
- 在ViewController中通過點擊屏幕,在ViewController中拿到block返回的數據
- (void)touchesBegan:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event
{/*__weak修飾的弱引用,如果指向的對象銷毀,那么指針會立馬指向nil(0x0)__unsafe_unretained修飾的弱引用,如果指向的對象銷毀,那么指針依然指向之前的內存地址,很容易產生'野指針'/'僵尸對象'*/__weak ViewController *weakSelf = self;[self.tools loadData:^(NSString *jsonData) {// NSLog(@"在ViewController拿到數據:%@", jsonData);weakSelf.view.backgroundColor = [UIColor redColor];}];
}
閉包
閉包的寫法:(參數列表) -> (返回值類型)
weakself?.view
- 如果前面的可選類型,沒有值,后面所有的代碼都不會執行
- 如果前面的可選類型,有值,系統會自動將weakself進行解包,并且使用weakself
閉包解決循環引用的三種方式:
- 方式一(寫法比較復雜):使用weak var weakSelf = self
- 注意:必須使用var來聲明,因為self可能會發生變化,如果使用let聲明,編譯器會報錯.
- 方式二(比較危險,當self指向nil時,可能會發生崩潰):tools.loadData {[unowned self] (jsonData) -> () in ... }
- 方式三(常用寫法):tools.loadData {[weak self] (jsonData) -> () in ... }
尾隨閉包:如果閉包作為方法的最后一個參數,那么閉包可以將()省略掉
- 普通寫法:tools.loadData ({[weak self] (jsonData) -> () in ... })
- 尾隨閉包的寫法一:tools.loadData() {[weak self] (jsonData) -> () in ... }
- 但是閉包中可以直接使用self,而不需要進行解包- 尾隨閉包的寫法二(系統默認寫法):tools.loadData {[weak self] (jsonData) -> () in ... }
注意:swift中的deinit()方法相當于OC中的dealloc方法,當對象銷毀時,會調用該函數.
OC中使用__weak修飾self,當對象銷毀時,self將指向nil(0x0)
OC中使用__unsafe__unretain修飾self,當對象銷毀時,self仍指向原內存地址,會產生"野指針"錯誤或"僵尸對象",這個寫法相當于swift中的unowned.
懶加載
懶加載的介紹
- swift中也有懶加載的方式.
- 蘋果的設計思想:希望所有的對象在使用時才真正加載到內存中
- 和OC不同的是,swift有專門的關鍵字來實現懶加載
- lazy關鍵字可以用于定義某一個屬性懶加載
懶加載的使用
- 格式:lazy var 變量: 類型 = {創建變量的代碼}()
// 懶加載的本質是,在第一次使用的時候,執行閉包
// 將閉包的返回值賦值給屬性
// lazy的作用是只會賦值一次
lazy var array: [String] = {() -> [String] inreturn ["abc", "def", "ghi"]
}()
也可以寫成下面這種方式
lazy var array: [String] = {return ["abc", "def", "ghi"]
}()