Swift語法(更新)


Swift語法(更新部分swift5)

簡介

  • 優於OC,快速,安全

  • 預編譯指令包括宏定義(OC用的太多了)

  • 取消了OC指針和不安全訪問的使用(看不到星星了)

  • 全部點語法,取消[ ]

  • 對Foundation框架做了很多改變,去除了NS,將絕大部分class轉換成struct結構體(為了考慮性能和安全性,絕大部分使用結構體來代替以前的類,但是在實際使用感覺不到)

  • 以前是OC調UIKit,現在就是Swift調UIKit,調用的SDK沒有變化

  • swift因為語法的嚴謹性可以讓很多錯誤提前遇到,這樣很少出現bug讓程序停在main導致無法找到

  • @UIApplicationMain是程序的入口

  • 只有.h沒有.m

  • 所有的代碼都包括在{}里,默認方法func都有縮進

  • OC語法的allocInit替換成()

  • OS12.2的各種版本,包括macos,tvos等等,已經包含了swift5庫,這意味着以前可能swift寫的代碼體積普遍大於OC,現在則整體做了體積壓縮

基礎

  • 基本寫法中不使self.,OC中的self.view之類寫法可以省略self. ,self在閉包或者編譯器提示的時候再使用

  • 分號是用來分割語句的,如果一行寫很多,就可以加分號,一般時候可以不加

    let a = 10; let b = 15 let aa = 10,bb = 15 
  • #function打印執行的函數
debugPrint(#function) 
  • 添加標記用到// MARK: - 選擇,如果是接下來要做的可以用// TODO:// FIXME:

變量和常量

  • let:常量,如果從沒被修改過,那就用let,不可變的更安全,所以盡量使用let,需要變化時候改var.像UI層的對象,采用let,但是還可以修改對應內的屬性,例如let UIView,隨后修改顏色,是因為本身地址沒變

  • var:變量

  • 可以自動推導類型(Option + Click)整數默認Int,小數默認Double,但是不會做隱試轉換,也就是說只要類型不同,無法直接計算

  • Swift對類型要求異常嚴格,任何不同類型的數據不能直接運算(哪怕是Int和Double),不會做一些自動的轉換來轉換成Double。Swift不存在基本數據類型,Int和Double都是結構體其實,強轉用Double(x)完成,或者在定義的時候直接指定變量的類型let x : Double = 10;(很少使用)

可選項(Optional)

  • 定義變量時,如果是可選的,表示可以有值,也可以是nil,用?

  • 強行解包 !,要少用,可能會崩

  • 最常見的錯誤:解包的時候發現nil。fatal error: unexpectedly found nil while unwrapping an Optional value

  • let可選的話,沒有默認值,需要賦值。var可選的話,默認值為nil

  • 可選項在參與計算時候必須解包,所以為避免很多!,一般會采用if let/guard let的方式進行判斷計算

邏輯分支(if)

  • 三目運算符:Int(x!) > 5 ? print("dayu5") : print("xiaoyu5") 或者 Int(x!) > 5 ? print("dayu5") : () 這樣就對后面的不作處理。()表示空執行。
  • swift中不存在非真既假,只有true和false

不強行解包的方法

  1. 強行解包的寫法:"??"。 一個簡單的三目,這樣無需做很多判斷,是不是xy都不得0了。注意?? 必須加個(),因為??操作符號的優先級低,是最后判斷的,所以不加()可能后面的都當做判斷不執行了。??寫法不需要解包了
    func demo(x: Int? , y : Int?) { print( (x ?? 0) + (y ?? 0) ) } 
  1. if let的寫法:iflet/ifvar連用就可以進行判斷,這個let的x1和y1作用於只在循環里,iflet/var也不需要解包了
        if let x1 = x , let y1 = y { print(x1 + y1) } 
  1. guard let else的寫法:和iflet相反,else里面一般是沒值return,之后才執行需要的代碼。guard可以降低分支層次,如果要執行的代碼很多,本來在if里執行,{}就多了一層,如果用guard守衛一定有值才執行,這樣層次就少了一層。guardLet很重要,也可以省略很多解包
        guard let x1 = x , let y1 = y else { print("x&y沒值") return } print(x1 + y1) 
  1. guardlet和iflet可以用同名變量接收。因為總會取名字,if let name = name這樣就可以,注意后面使用的時候用非空的那個!並且iflet和guardlet可以依次判斷,先判斷是一個字典,再拿字典的數組,在判斷數組的值,可以一條線判斷出來。

總結

    func demo() { let name: String? = "張三" let age: String? = "18" // 目的: 讓可選項在不強行解包!的情況下做計算 // 方式1:采用?? print("方式1" + (name ?? "") + (age ?? "")) // 方式2:采用if let if let name = name, let age = age { print("方式2" + name + age) } // 方式3:采用guard let guard let nameA = name, let ageA = age else{ return; } print("方式3" + nameA + ageA) } 

循環

switch循環

  • OC中分支必須是整數,每一個語句都需要break,如果要定義局部變量需要{}

  • Swift可以任意判斷,一般不需要break,多值判斷用逗號,所有分支都至少需要一條指令,如果什么都不干,才要寫break

    func demo(name : String) { switch name { case "guoguo","aixin": print("guoguo") default: print("shit") } } 

for循環

  • swift取消了i++++i
    for i in 0...5 { } // ...是閉區間,..<是開區間 for i in 0..<5 { } 
  • 反序遍歷:
     for i in (0..<10).reversed() { } 

字符串

  • 用String,是一個結構體,具有絕大多數NSString功能,支持直接遍歷

  • 遍歷:

    func demo3() { let str = "wowosnshi是" for s in str { print(s) } } 
  • 長度:

    // 返回指定編碼對應的字節數,每個漢字三個字節 print(str.lengthOfBytes(using: .utf8)) // 返回真正字符串長度 print(str.count) 
  • 拼接:要注意可選項拼接不解決會帶上Optional,剩下的都可以拼接,再也不用看StringWithFormat了

    let name = "AA"

    let age = 19

    let title : String? = "sss"

    print("(name)(age)(title ?? "")")

  • 格式化:

    let h = 8 , m = 10, s = 44 // OC中用stringWithFormat格式化日期,Swift中可以 let strDate = String(format: "%02d-%02d-%02d", h,m,s) print(strDate) 
  • 4.2截取字符串
     func demo3() {
        let str = "hello world" // 截取前三個字符 print(str.prefix(3)) // 截取后三個 print(str.suffix(3)) // 截取3-6范圍 let start = str.index(str.startIndex, offsetBy: 3) let end = str.index(str.startIndex, offsetBy: 6) print(String(str[start..<end])) } 

數組:

  • 就是中括號,注意數組的類型,並且基本數據類型不需要包裝,可以直接方數組里,如果類型不一樣(混合數組,但是基本不用),自動推導[NSObject]。在Swift中還有一個[AnyObject類型],標示任意對象,因為在Swift中一個類可以沒有任何父類。
   遍歷: let array = ["張三","李四","王五"] // 遍歷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() { } 
  • 增刪改:

    array.append("AA") array[1] = "BBB" array.remove(at: 2)
  • 合並:用“+”號。但是要合並的數組的兩個類型必須一致。

字典

  • 一般是[String:Any]

  • 增刪改:和數組都類似,就是兩個字典合並不像數組直接相加,而是需要遍歷

函數

  • Swift的類,結構體,枚舉三種都有構造函數,都可以有方法,就像OC的類。枚舉再swift變化很大,一般開發不會用到太高級語法。

  • 外部參數,當外部參數用_替代的時候,會在外部調用的時候忽略形參名

    func demo5(num1 a: Int,num2 b: Int) -> Int { return a + b } 
  • 函數的默認值(OC不具備),這個使Swift比OC靈活很多很多,一個方法可以做很多事,因為OC會有各種參數和組合,Swift只需寫一個最多的參數,然后不需要的設定默認值就是了
  • 無返回值 :直接省略 () Void都可以

  • 閉包:類似Block,比Block還廣泛。OC中Block是匿名函數,Swift中函數是特殊的閉包。閉包在整個開發中和Block的應用場景一樣。用於控制器/自定義視圖/異步執行完成的回調。這些回調的特點就是都是以參數回調處理結果,返回值為Void。

    let biBao = { (x: Int) -> Int in return x + 100 } print(biBao(10)) 
  • GCD:將任務添加到隊列,指定執行任務的函數。任務就是Block/閉包,隊列以同步/異步的方式執行。

    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)") }

*尾隨閉包:如果函數的最后一個參數是閉包,那么參數就省略了,最后一個參數直接{}大括號包裝

面向對象

基本構造函數

  • ()就是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中其實就是allocInit,在swift中目前只用init就行,一般的對象init時候會在前面默認加上override,意為重寫,重寫最大的特點是方法里面可以調用super.init()來實現父類的這個方法,而且super.init()是隱式的,意味着如果不寫也會調用,但是程序員其實最害怕的是我什么都沒寫?為什么調用了?,所以為了完整的自我實現面向對象思想,還是要自己添加super.init()

重載構造函數

  • 重載構造函數:(重寫是父類有這個方法,override。重載是函數名相同,參數和個數不同。init就重寫,init+參數就重載。OC是沒有重載的!都是initWithXXXXX)。重載其實是最基本的方式,OC沒有其實很low,但是Swift有。

  • 重載是所有方法都可以實現的,不局限於構造函數,例CGRect(,可以看到很多重載的實現

  • Nsobject類的isa屬性就是為了記錄當前狀態的對象是什么的,就是一個student對象,雲雲。在swift中打p可以打印類內容

KVC構造函數

  • KVC是OC特有的,是運行時特性,在運行時動態的發消息賦值,所以在KVC構造時候,super.init()應該先調用,隨后再setValueForKey保證都創建完畢再調用。調用setValueForKey的時候,會判斷賦值的對象是不是對象。要是對象的話就去判斷是否已創建,沒有創建則實例化。要是基本數據類型則不作處理,所以基本數據類型必須指定初始值來分配空間才行

  • 運行時可以獲取到一個對象的所有屬性方法等等,獲取到了屬性可以KVC進行字典轉模型,這是所有第三方轉模型插件的基礎,獲取到了方法,可以動態的發送各種方法。在swift中,如果一個基本變量Int什么的用?修飾了,可選了。此時運行時是找不到的,所以KVC會崩潰,同時,如果修飾了private屬性,運行時也是找不到,KVC也會崩潰。其實在OC中,private是基本不用的,而且存不住任何私有,都可以被運行時找出來。但是swift就是真的藏起來了,找不到這個屬性和方法了外界

  • 一般在模型中加個?,然后用KVC實現(先調用init因為是運行時機制)

  • 模型中屬性定義:基本數據類型 = 0,對象設置?

運行時中,基本類型設置? 屬性設置私有都會讓運行時拿不到,此時kvc就會出錯。

  • 標准寫法
    @objc var name: String? @objc var age: Int = 0 init(dict: [String : Any]) { super.init() setValuesForKeys(dict) } override func setValue(_ value: Any?, forUndefinedKey key: String) { } 
  • 如果子類沒有重寫父類方法,調用的時候就會直接調用父類的方法。當繼承一個類,就繼承所有屬性和方法,包括KVC。當PERSON寫好了KVC后,STudent即便什么也不寫,也有父類的方法

便利構造函數

  • 關鍵字Convenience

  • 目的:條件判斷,只有滿足條件才實例化對象,防止不必要的內存開銷,簡化對象創建。本身是不負責屬性的創建和初始化的。

  • 說白了就是有些函數 帶個? 返回值可以返回空,這就是便利構造函數的功勞。由於函數里多為判斷,是否要構造,不符合構造條件就不創建,而創建的過程需要調用別的構造函數才能實現,所以最后都要調用self.init(xxxxxxx)

  • 主要用於條件檢查和控件創建。

  • 在控件創建的時候,就不用?了,因為我就是為了創建控件的。

  • 不能被繼承,不能被重寫

  • deinit:類似OC的Dealloc

  • 便利構造函數 + 分類可以省略抽取很多代碼。例如給UITextField/UIButton寫分類,然后寫便利構造函數,方便。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM