iOS - Swift Swift 語言新特性

一、Swift 2.0 帶來哪些新變化

  • 常規變化:編程

    • 一、OS X 10.十一、iOS 9 和 watchOS 2 SDK 採納了一些 Objective-C 的特性用來提升 Swift 的編程體驗, 如可空性、類型化集合和一些別的特性。swift

    • 二、編譯器對冗餘的協議一致性,未被使用的綁定值以及能夠設爲常量的變量這些狀況目前會給予警告或報錯。數組

    • 三、修復了跨文件協議遵循時符號不可見或者重複的錯誤。閉包

    • 四、Swift 語言的調用約定更加智能,可以理解 API 所發生的變化和 Swift 所給出的警告。併發

    • 五、便利的可失敗構造器(failable initializer)能夠先返回 nil,而沒必要首先調用 self.init。這是有利的一 面,但指定了構造器在返回 nil 前仍要給全部字段初始化。app

    • 六、find 函數更名爲 indexOfsort 則變成了 sortInPlacesorted 變成了 sort框架

    • 七、String.toInt() 重名爲 Int(String) 的可失敗構造器,由於構造器語法更適合類型轉換。異步

      • String 類型再也不遵循 SequenceType,可使用 .characters.utf8.utf16 對應字符集的運算。容許對泛型添加公共擴展。async

      • 字符串長度長度計算由 count(String) 變爲 String.characters.count編程語言

      • 字符串裁剪由 code.substringToIndex(advance(code.startIndex, 6)) 變爲 let endIndex = code.startIndex.advancedBy(6) code.substringToIndex(endIndex)

    • 八、標準庫中重構了不少泛型的全局函數(如 mapfiltersort),採用協議擴展方式增長這些方法。這個好處是對於其餘的關聯類型能很好的適配。

      • 非泛型類類型能夠繼承泛型類(強制類型參數固定)。

      • 修復了 Swift 中泛型需求打印時 「T==T」 的錯誤。

      • 在泛型函數中聲明瞭類型參數可是在函數中沒有使用時將產生一個編譯時錯誤,例如:

      • func foo<T> () {} // error:generic parameter ’T’ is not used in function signature

    • 九、基本上可使用 enum SomeEnum<T,U,V> 來聲明 multi-payload 風格的枚舉,這樣就能正常運行。這用來提示未完成的指令寄存器(IR)引起的錯誤。

      • 在 Objective-C 的枚舉類型導入到 Swift 時,已經廢棄的枚舉元素將不會影響可用元素的使用,這個可能須要 Swift 中一些枚舉名稱的改變。

      • 從 C 中導入的枚舉類型都表示爲 RawRepresentable,這包括哪些沒有被聲明爲 NS_ENUMNS_OPTIONS 枚舉值,全部這些枚舉類型中的 value 屬性都須要重名爲 rawValue.

    • 十、方法和函數如今使用一樣的參數命名規則了,咱們能夠用 「_」 符號來省略一個外部的參數名,爲了簡化使用,用來指定參數名的簡化符號 「#」 被移除,由於 Swift 爲默認參數提供了特殊的規則:

      • 聲明:

        func printFunction(str:String, newline:Bool)
            func printMethod(str:String, newline:Bool)
            func printFunctionOmitParameterName(str:String, _newline:Bool)
      • 調用:

        printFunction("hello", newline:true)
            printMethod("hello", newline:true)
            printFunctionOmitParameterName("hello", true)
    • 十一、條件循環語句 do/while 循環被重名爲 repeat/while。關鍵字 do 目前用來引入一個新的做用域(這對新引進的錯誤處理和 defer 關鍵字很重要)。

      Swift 1.2:
      
              do {
                  ...
              } while <condition>
      
          Swift 2.0:
      
              repeat {
                  ...
              } while <condition>
    • 十二、打印語句的改變,在 Swift1 中,有 println()print() 兩個在控制檯打印語句的方法,前者是換行打印,後者是連行打印。在 Swift 2 中,println() 已成爲過去,取而代之的是他倆的結合體。

      Swift 1.2:
      
              func print(<stuff to print>)
              func println(<stuff to print>)
      
          Swift 2.0:
      
              func print(<stuff to print>, appendNewline:Bool = true)
      
          若是你想作連行打印,如今須要這樣寫:
      
              print("我要換行!", appendNewline: true)
    • 1三、Swift 的文本註釋(doc comments)換成了 Markdown 語法格式,與 Playgrounds 統一(Playgrounds 註釋格式源於功能有限的 reStructured Text)。

      參數縱覽語法:
      
              ‐ Parameters:
                  ‐ x:...
                  ‐ y:...
      
          單獨參數語法:
      
              ‐ parameterx:...
              ‐ parametery:..
      
          返回值:
      
              ‐ returns:...
      
          其餘須要在 QuickHelp 中高亮的語法字段,能夠參考 Markdown 語法。
    • 1四、在 Swift 中增長了 @objc(propertyName) 屬性,當該屬性導入到 Objective-C 時能夠採用這個 propertyName 做爲 getter/setter 訪問器的默認名,例如:

      class MyClass:NSObject {
      
              // Objective‐C 屬性被命名爲 「theProperty」
              @objc(theProperty) property:String
      
              // Objective‐Cgetter 訪問器被命名爲 「theProperty」
              // Objective‐Csetter 訪問器被命名爲 「setTheProperty:」
          }
    • 1五、註冊通知由

      var types = UIUserNotificationType.Badge | UIUserNotificationType.Sound | UIUserNotificationType.Alert
              var acceptAction = UIMutableUserNotificationAction()
      
              acceptAction.identifier = "ACCEPT_IDENTIFIER"
              acceptAction.title = "Accept"
              acceptAction.activationMode = UIUserNotificationActivationMode.Foreground
              acceptAction.destructive = false
              acceptAction.authenticationRequired = false
      
              var inviteCategory = UIMutableUserNotificationCategory()
      
              inviteCategory.identifier = "INVITE_CATEGORY"
              inviteCategory.setActions([acceptAction], forContext: UIUserNotificationActionContext.Default)
              inviteCategory.setActions([acceptAction], forContext: UIUserNotificationActionContext.Minimal)
      
              var categories = NSSet(object: inviteCategory)
              var mySettings = UIUserNotificationSettings(forTypes: types, categories: categories as Set<NSObject>)
      
              UIApplication.sharedApplication().registerUserNotificationSettings(mySettings)
              UIApplication.sharedApplication().registerForRemoteNotifications()
      
          修改成:
      
              let acceptAction = UIMutableUserNotificationAction()
      
              acceptAction.identifier = "ACCEPT_IDENTIFIER"
              acceptAction.title = "Accept"
              acceptAction.activationMode = UIUserNotificationActivationMode.Foreground
              acceptAction.destructive = false
              acceptAction.authenticationRequired = false
      
              let inviteCategory = UIMutableUserNotificationCategory()
      
              inviteCategory.identifier = "INVITE_CATEGORY"
              inviteCategory.setActions([acceptAction], forContext: UIUserNotificationActionContext.Default)
              inviteCategory.setActions([acceptAction], forContext: UIUserNotificationActionContext.Minimal)
      
              let categories = NSSet(object: inviteCategory) as! Set<UIUserNotificationCategory>
              let mySettings = UIUserNotificationSettings( forTypes: [.Alert, .Badge, .Sound], categories: categories)
      
              UIApplication.sharedApplication().registerUserNotificationSettings(mySettings)
              UIApplication.sharedApplication().registerForRemoteNotifications()
  • 內部的可見性:

    • 這解決了單元測試中的一個較大的難點。之前的作法:

      • Swift 文件包含在 test target 中。如今不一樣的模塊中有重複的類的定義,出現沒法將 「X」 轉換爲 「X」 這樣很是可怕的錯誤,有時會沒法執行特定的測試。

      • 在測試中引入引入主程序(main program)做爲一個模塊。如今一切都聲明爲 public,因此對於測試來講都是可見的,有時候也包括應該聲明爲 private 的內部細節。

    • 如今能夠啓用 testability,它就像 C# 中的 InternalsVisibleTo。主應用程序目標模塊的內部細節對測試模塊可見。

      • 在對應用或框架的測試設置中,啓用 testability。
      • 在單元測試中,使用 @testable import { ModuleName }
    • 這將致使測試忽略某些優化行爲並保留稍後導入到測試模塊中的那些內部符號。官方文檔警告說,因爲阻止了某些優化,所以這隻適用於調試和測試版本。

  • 模式匹配:

    • Switch 語句的模式匹配(pattern matching)語法和 「if let ..., .... where」 語法一直在推廣。能夠在任何控制流中使用逗號操做符和 where 條件語句。還可使用新的 case 條件語句,例 - 如:if case .Silly(let a) { }。還有一種用於 Optional<T> 的特殊形式:if case let a? = anOptional { }

      • 模式匹配在循環語句中也可使用:for case let thing? in array { }。 這又是值得單獨成文的另外一個特性。

      • 類型標註不能用於模式匹配,而須要做爲標註聲明的一部分:

        • 這意味着,之前的這樣的寫法:

          • var (a:Int, b:Float) = foo()
        • 須要被重構爲:

          • var (a, b):(Int, Float) = foo()
        • 其實這個改動緣由是爲了和元組用法相區分。

  • 錯誤處理:

    • NSError 變成 throw。這不是咱們一向所認識的異常,這是一個使函數提早返回 Result<T, Error> 的操做,單隱藏了全部提早返回的對象,也隱藏了錯誤解析(error unwrapping)過程等內容。

      let systemAttributes: [NSObject: AnyObject]?
      
          do {
      
              systemAttributes = try NSFileManager.defaultManager()
                                                  .attributesOfFileSystemForPath(documentDirectoryPath.last!)
      
          } catch _ {
      
              systemAttributes = nil
          }
      
          它完美地與 Objective-C 進行互操做,Swift 語言中,將標記爲 throws 的方法做爲選擇器。這是使用 NSError 的方法,
          -(BOOL or nullable type)someMethodTakingParam:(type)param error:(NSError **),
          這種樣式會自動引入標記爲 throws 的方法。
    • 應該明白的是這並不像 Java 中已經被檢查過的異常(checked exception)那樣。Swift 語言並不關心異常的類型,或者處理或者不處理。這又是值得單獨成文的另外一功能特性。

  • guard 語句塊:

    • 顯式地聲明你要恆成立的條件語句,恆成立時跳過整個 guard 語句。這樣作的好處是綁定在 guard 語句的變量在函數的其餘部分也可用。這就避免了將全部的東西都圍繞一條 if 語句嵌套使用來解析(unwrap)可選類型的變量。執行到函數中 guard 語句中的 else 部分,函數必定會退出並拋出異常。也可能會調用帶有 @noreturn 標記的函數。
  • Defer 關鍵字:

    • 關鍵字 defer 也很重要,由於它能夠取代傳統 C 風格的 「if (err) goto cleanup」。得到資源後接着就是 defer { release_resource() }。而後無論函數返回結果如何,得到的資源都將被清理。這也意味着資源的釋放緊隨獲取資源以後。這看起來不起眼兒,實則很重要。
  • NS_OPTIONS 和 OptionSetType:

    • NS_OPTIONS 類型如今遵循 OptionSetType 協議,這樣能夠避免 set 樣式的接口調用。位操做枚舉(bitwise enumeration)與數組風格的語法相結合,而不使用管道符 「 | 」 按位操做,而且具備全部範圍的集合操做功能。檢查一下是否具備 contains 功能的標誌,或可以執行像 isSubsetOfisDisjointWith 等這樣集合操做的其餘功能。這是顯著的改進,表達了不直接對位進行操做的意願。

    • 避免採用以下位運算的調用方式:

      // Swift1.2:
      
              object.invokeMethodWithOptions(.OptionA | .OptionB)
              object.invokeMethodWithOptions(nil)
      
              if options & .OptionC == .OptionC {
      
                  //.OptionC 被設置
              }
    • 選項設置支持字面量語法和 set 樣式的調用,如 contains:

      object.invokeMethodWithOptions([.OptionA, .OptionB])
          object.invokeMethodWithOptions([])
      
          if options.contains(.OptionC) {
      
              //.OptionC is set
          }
    • 這種變化意味着位操做枚舉實際上再也不是枚舉了。將這些位操做枚舉聲明爲結構體,實現 OptionSetType 協議,提供 rawValue 屬性。而且建立值做爲結構體的靜態成員。Swift 便會搞定其他的一切,自動提供全部集合的操做。

    • 在 Swift 中一個新的 Option 設置類型能夠採用結構體遵循 OptionSetType 協議的方式編寫。若是該類型中指定了一個 rawValue 屬性和 static let 的常量定義,那麼標準庫將會爲其餘選項提供默認實現:

      structMyOptions:OptionSetType {
      
          let rawValue:Int
              static let TuringMachine = MyOptions(rawValue:1)
              static let LambdaCalculus = MyOptions(rawValue:2)
              static let VonNeumann = MyOptions(rawValue:4)
          }
      
          let churchTuring:MyOptions = [.TuringMachine, .LambdaCalculus]
  • 協議擴展:

    • 在 Swift 1.0 時代,協議(Protocol)基本上相似一個接口,定義若干屬性和方法,供類、結構體、枚舉遵循和實現。在 Swift 2.0 中,能夠對協議進行屬性或者方法的擴展,和擴展類與結構體相似。,包括與類型約束有關的通用協議。還能夠本身提供協議的默認實現。這讓咱們開啓了面向協議編程的篇章。先前,你不能,你說:「我要使用方法 X 來擴展 CollectionType,但只有集合中的類型知足某些條件才能夠」。如今,你能夠這麼作,而且不少像 map,filter 和 sort 這樣的全局函數已經進行了擴展。這樣就解決了不少痛點,這也是值得單獨成文的內容。同時,要看看 WWDC 的面向協議編程(Protocol Oriented Programming)瞭解一些細節。

    • Swift 中,大多數基礎對象都遵循了 CustomStringConvertible 協議,好比 Array、Dictionary(Swift 1.0 中的 Printable 協議),該協議定義了 description 方法,用於 print 方法打印對象。如今咱們對該協議擴展一個方法,讓其打印出大寫的內容:

      var arr = ["hello", "world"]
      
          print(arr.description)         // "[hello, world]"
      
          extension CustomStringConvertible {
              var upperDescription: String {
                  return "\(self.description.uppercaseString)"
              }
          }
      
          print(arr.upperDescription)    // "[HELLO, WORLD]"
    • 若是在 Swfit 1.0 時代,要想達到上述示例的效果,那麼咱們須要分別對 Array、Dictionary 進行擴展,因此協議的擴展極大的提升了咱們的編程效率,也一樣使代碼更簡潔和易讀。

  • available 檢查:

    • 做爲 iOS 開發者,誰都但願使用最新版本 iOS 的 Api 進行開發,省事省力。但經常事與願違,由於咱們常常須要適配老版本的 iOS,這就會面臨一個問題,一些新特性特性或一些類沒法在老版本的 iOS 中使用,因此在編碼過程當中常常會對 iOS 的版本作以判斷,就像這樣:

      if NSClassFromString("NSURLQueryItem") != nil {
              // iOS 8 或更高版本
          } else{
              // iOS8 以前的版本
          }
    • 以上這只是一種方式,在 Swift 2.0 以前也沒有一個標準的模式或機制幫助開發者判斷 iOS 版本,並且容易出現疏漏。在 Swift 2.0 到來後,咱們有了標準的方式來作這個工做:

      if #available(iOS 8, *) {
      
              // iOS 8 或更高版本
              let queryItem = NSURLQueryItem()
      
          } else {
              // iOS8 以前的版本
          }
    • @available 屬性自 Swift 1.2 就存在了而且後續支持得很好。添加了一個新的陌生語法 if #available(),爲處理版本檢查提供了支持。而不是插入你喜歡的方法。

    • 遺憾的是你不能只聲明一個屬性 UISearchController 並將 target 設置爲 iOS 7,而後只容許訪問類中的屬性。Swift 但願整個類的定義均可以或者不能夠。也能夠再也不採用協議,除非支持 target設置中全部的操做系統版本,除非將整個類標記爲只在更新的操做系統版本可用。

    • 這意味着使用 if #available() 存在單獨的子類和對建立適當對象的保護。儘管如此,我我的仍是發現了一個 Bug,應用在 iOS 4.0-4.1 發生崩潰,因爲編譯器沒有發出警告,方法只在 iOS 4.2 才引入,所以我猶如與定時炸彈相伴。

  • C 函數指針:

    • Swift 如今可使用 C 函數指針,CFunctionPointer<T -> U> 類型被移除,C 函數如今使用新的 @convention(c) 屬性聲明,和其餘函數類型同樣,@convention(c) T -> U 是一個非空的除非是它是可選的。任何全局函數,嵌套函數和不捕獲狀態的閉包均可以做爲一個 C 函數指針直接傳遞。你也能夠調用來自 C 程序的函數。

    • 你能夠顯示地使用新屬性 @convention(c),表示函數應該使用 C 調用約定,簡單痛快!儘管我想不出在此對塊(block)的支持有何用,做爲所發生變化的一部分,@objc_block 也被刪掉了,使用 @convention(block) 取而代之。@convention(swift) 默認支持全部函數和閉包。

  • API 審計:

    • 大量的 API 已經進一步進行了審計而更合理。舉幾個例子:

      • UITableView 的 dequeueReusableCellWithIdentifier 方法如今返回 UITableViewCell? 類型的對象。

      • UIKit 的屬性如今也被聲明爲了實際的屬性。

      • translatesAutoresizingMaskToConstraints = false 代替了 setTranslatesAutoresizingMaskToConstrains(false)

  • 庫:

    • 這並非編程語言所特有的。iOS 9 含有不一樣版本的 Swift 標準庫,而且在將來系統中將添加修正後的 Swift 標準庫。結合新的 App Thining 技術,下載過程當中蘋果商店會將 Swift 標準庫剝離出去的。我仍然在追根溯源地探求這到底是如何工做的。
  • 遺漏:

    • 明顯的一個遺漏是處理異步代碼。蘋果公司爲咱們提供了 GCD,這是一個強大的基礎類庫,能夠構建不少異步操做和併發原語。然而,這些天咱們作的每件事,構建用戶接口和 API 都須要考慮異步性和併發性。咱們把一個文件讀操做鎖定一段時間,對用戶來講整個世界就都靜止了。這是個持續的痛點,不是多大的事兒,但若是常常性地天天重複,恐怕也是不行的。C# 和 JavaScript 都採用了 async/await 來爲異步代碼提供一流的語言支持。我想不少人都想知道,Swift 會提供什麼樣的語法糖來幫助咱們在實現異步操做方面確保正確性。

二、Swift 2.2 新特性

  • 容許更多的關鍵字用作參數名:

    • 好的參數名對於提升代碼可讀性很重要。在 Swift 中不少關鍵字好比 in,repeat,defer 都不能用作參數名。2.2 中,除了少數修飾參數的關鍵字外都將容許用做參數名。

      swiftnew1

  • 爲 Tuples 增長對比操做符:

    • 當前,Tuples 的數據不能使用 == 操做符,2.2 中將支持 Tuples。

      swiftnew2

  • 關聯已存在類型時,再也不使用 typealias:

    • typealias 如今有兩個用處:
      • 爲一個已經存在的類型取個別名
      • 在協議中做爲一個類型的佔位名稱
    • 代碼以下:

      swiftnew3

    • 這是兩種徹底不一樣的用法,不該該用同樣的關鍵字。2.2 中將第一種狀況時,啓用新的關鍵字 associatedtype

  • 函數簽名將包括參數名:

    • 一個函數有相同的函數名,參數名不一樣有多個重載很常見。當有多個重載時,在調用時可以經過參數名來區別。可是在獲取類型時,卻不包括參數名。

    • 舉例 UIView 中有這麼幾個方法:

      swiftnew4

    • 使用時能夠經過參數名區分:

      swiftnew5

    • 可是這樣使用時卻會報錯,2.2 中將會解決這個問題。

      • let fn = someView.insertSubview // ambiguous: could be any of the three methods
  • 一個新的方法生成 selector:

    • 如今爲了生成 OC 下使用的 selector 只能使用字符串生成,沒有類型檢查,很容易形成失誤。將提供一個 #selector() 方法生成 selector,以下:

    • let sel = #selector(UIView.insertSubview(_:at:)) // produces the Selector "insertSubview:atIndex:" 增長 #if swift 語法判斷當前 swift 版本

    • 使用以下:

      swiftnew6

相關文章
相關標籤/搜索