足球资料库数据/孙祥/nba五佳球/足球直播哪个平台好 - cctv5今日现场直播

首頁 > 知識庫 > 正文

Swift編程的15個技巧
2016-03-04 15:25:12   來源: mengyidan1988   評論:0 點擊:

相對于Objective-C,Swift是一種編譯代碼時速度更快、安全性與可靠性更高、同時具有可預測性的語言。下面我們列出了在實踐中使用這種新語言時,所獲取一些Swift使用技巧。這些技巧有助于讓開發者編寫出更干凈的代碼,并能幫助更熟悉Objective-C的程序員適應Swift編程,同時適用于在Swift上具有各種背景經歷的人,請繼續往下看。 章節的順序是按照使用者對Swi
相對于Objective-C,Swift是一種編譯代碼時速度更快、安全性與可靠性更高、同時具有可預測性的語言。下面我們列出了在實踐中使用這種新語言時,所獲取一些Swift使用技巧。這些技巧有助于讓開發者編寫出更干凈的代碼,并能幫助更熟悉Objective-C的程序員適應Swift編程,同時適用于在Swift上具有各種背景經歷的人,請繼續往下看。

章節的順序是按照使用者對Swift的熟悉程度來排列的。第一部分是針對不太了解Swift的人,第二部分是針對初級入門者,而最后一部分是對于已在使用Swift的人。

你應當了解,但有可能不知道的Swift技巧
提高常數的可讀性
在Swift中使用struct的簡潔辦法,就是在應用中制作一個適用所有常數的文件。由于Swift允許我們嵌用下面的結構,這種辦法非常有用:
import Foundationstruct Constants {    struct FoursquareApi {        static let BaseUrl = "https://api.foursquare.com/v2/"    }        struct TwitterApi {        static let BaseUrl = "https://api.twitter.com/1.1/"    }    struct Configuration {        static let UseWorkaround = true    }    }

嵌套讓我們可以為常數生成一個命名空間(namespace)。例如:我們可以使用Constants.FoursquareApi.BaseUrl來訪問Foursquare的BaseUrl常數,這樣會使得數據可讀性更高,并為相關的常數提供一系列封裝。

為了提高性能,要避免NSObject與@objc
Swift允許我們將分類進行擴展,從NSObject到獲取對象的Objective-Cruntime系統功能。還允許我們用@objc來注釋Swift方法,以便在Objective-C runtime中使用。

支持Objective-C runtime,代表著系統不再通過通過靜態或vtable分配,而是動態分配來調用方法。結果就是:在調用支持Objective-C運行的方法時,性能損失會高達四倍。在實際應用中,這種情況對性能的影響也許微不足道,不過這樣一來,我們就知道通過Swift執行方法調用要比使用Objective-C快四倍。

在Swift中使用方法調配(Method Swizzling)
方法調配是替換一個已存在的方法實現。如果對此不熟悉,可以閱讀這篇文章。Swift優化后,不再像Objective-C中那樣,在runtime尋找方法的位置,而是直接調用內存地址。因此默認情況下,在Swift類中調配無法起效,除非:
  • 用動態關鍵字禁用這種優化。這是最佳選擇,如果數據庫完全以Swift構建的話,這種選擇也是最合理的方式。
  • 擴展NSObject。如果單純為了方法調配的話,不要用這種方式(而要采用動態的)。需要了解:在將NSObject作為基礎類的已存在類中,方法調配是有效的,不過最好使用動態選擇的方法。
  • 在要調配的方法中使用@objc注釋。如果我們想要調配的方法同時也需要使用Objective-C的代碼,那么這種方法是最合適的。

引用

更新:根據要求,我們增加了一個完全使用Swift的調用樣例。在這個樣例中仍需要Objective-C runtime,不過類并非繼承自NSObject,方法也未標記成@objc。

import UIKitclass AwesomeClass {    dynamic func originalFunction() -> String {        return "originalFunction"    }      dynamic func swizzledFunction() -> String {        return "swizzledFunction"    }}let awesomeObject = AwesomeClass()print(awesomeObject.originalFunction()) // prints: "originalFunction"let aClass = AwesomeClass.selflet originalMethod = class_getInstanceMethod(aClass, "originalFunction")let swizzledMethod = class_getInstanceMethod(aClass, "swizzledFunction")method_exchangeImplementations(originalMethod, swizzledMethod)print(awesomeObject.originalFunction())  // prints: "swizzledFunction"

入門者所需的Swift技巧
清理異步代碼
Swift在編寫補齊函數(completion function)上語法非常簡潔。在Objective-C中有completion block,不過出現的很晚,語法也有些粗糙,如下:
[self loginViaHttpWithRequest:request completionBlockWithSuccess:^(LoginOperation *operation, id responseObject) {  [self showMainScreen];} failure:^(LoginOperation *operation, NSError *error) {  [self showFailedLogin];}];

在Swift中有一種更簡單的新型閉包語法。任何將閉包作為末尾參數的方法都可以使用Swift的新語法,讓回調更簡潔,如下:
loginViaHttp(request) { response in  if response.success {    showMainScreen()  } else {    showFailedLogin()  }}

控制對代碼的訪問
應該堅持用合適的訪問控制修飾符(access control modifier)來封裝代碼。如果封裝的好,無需記下思維過程,也無需詢問代碼編寫者,就能理解這段代碼是如何交互的。

Swift常見的訪問控制機制有三種:私人訪問、內部訪問和公共訪問。不過Swift中并沒有常見于其它面向對象語言中的protected訪問控制修飾符。為什么會這樣呢?那是因為在子類中通過新的公共方法或屬性,就可以顯示protected方法或屬性,因此實際上保護是無效的。而且由于從任何地方都能重寫,因此protected并未給Swift編譯器開啟優化的機會。最后,由于protected阻止子類helper訪問子類能夠訪問的信息,會讓封裝變差。想要了解Swift團隊關于protected更多的想法,請點擊這里查看。

實地實驗與驗證
Playground是蘋果在2014年隨Swift一起推出的一款交互式編程工具,可以用來測試及驗證想法、學習Swift、與其他人分享概念。無需創建新項目,只需在運行Xcode的時候將playground選中就可以了。

也可以在Xcode中創建新的playground:



一旦有了playground,在編程時便能實時看到結果:



通過Playground可以將想法原型化,并以代碼形式展示,同時還不會造成開啟新項目的額外開銷。



安全地使用可選值
可選值(optional)屬性指的是這個屬性或有效值或無值(為空)。通過可選值的名稱+感嘆號,格式為optionalProperty!,便可隱式解開一個可選值。 一般這是需要避免的,因為感嘆號暗示著“危險”。

不過有些情況下,隱式解開可選值是可以接受的。比如IBOutlets就是默認將可選值隱式解開的(在Interface Builder中點擊拖拽時),因為UIKit假定我們是將對象接口(outlet)與IB連接起來的。IBOutlets在初始化之后已經設置好了,因此接口是可選值的,同時根據Swift規則,在初始化之后所有非可選值的屬性必須有值。另一個通過名稱獲得UIImage的案例是存在于我們的asset catalog之中的:
let imageViewSavvyNewYearsParty = UIImageView(image: UIImage(named: "Savvy2016.png")!)

將默認值設置為常量屬性,在不隱式打開可選值的情況下是無法做到的。也就是說,!仍舊代表“危險!”但在這種情況下,是告知我們需要當心錯誤,并在運行前驗證名稱是否相符。一般來講,假如我們必須使用空值,app就會有崩潰的風險。用!來隱式打開值會讓編譯器知道,我們已經知道在運行時可選值不會為空。在幾乎所有場景之中,這都是帶有賭 博性質的,因此最好使用if let模式來確定可選值是有有效值還是為空:
if let name = user.name {    print(name)} else {    print("404 Name Not Found")}

拋棄數字對象(NSNumber)
Objective-C使用C primitives來代表數字,用Foundation Objective-C API來提供數字對象類型,將primitives裝箱拆箱。需要在primitives與對象類型之間切換時,代碼會像 [array addObject:@(intPrimitive)]和[array[0] intValue]這樣。Swift就不會有這種不當的機制。相對的,我們實際上可以向Swift字典和數組中添加Int / Float / AnyObject值。
下面是代替數字對象的一些Swift最常用的類型:
  • Swift: Objective-C
  • Int: [NSNumber integerValue]
  • UInt: [NSNumber unsignedIntegerValue]
  • Float: [NSNumber floatValue]
  • Bool: [NSNumber boolValue]
  • Double: [NSNumber doubleValue]

在用Objective-C編寫的不同類型中,我們仍可以用數字對象來進行轉換,不過在Swift中,轉化值的常用方式是使用目標類型的構造函數。舉個例子,如果我們從API中獲得一個數字userID,將其在數字對象中打開并顯示為字符串,在Objective-C中需要輸入[userId stringValue]。而在Swift中數字對象不再使用(除非要向后兼容Objective-C),因為在Swift中,數字結構與在Objective-C中限制不同。
引用

注意:在使用Objective-C或依賴沒有Swift封裝的舊式代碼庫中,可能仍得使用數字對象。在這種情況下,數字對象API基本沒什么變化。

相反,在Swift中我們通過構造函數進行等效轉換。舉個例子,如果userID是一個Int,而我們想要字符串的話,只需通過String(userId)進行轉換。這比一直將數字對象裝箱拆箱容易多了,不過數字對象所提供的各種各樣的轉換,確實讓API簡單易用。

通過默認參數減少樣板文件代碼
在Swift中,函數自變量現在可以有默認值了。這些默認的參數減少了雜亂程度。如果某函數的被調用者選擇使用默認值,由于默認參數可以省略,這個函數調用就能更短一些了。例如:
unc printAlertWithMessage(message: String, title: String = "title") {   print("Alert: (title) | (message)")}printAlertWithMessage("message") // prints: Alert: title | messageprintAlertWithMessage("message", title: "non-default title") // prints: Alert: non-default title | messagex

為更熟練的使用者提供的一些Swift技巧
通過Guard來驗證方法
Swift的guard語句讓代碼更簡潔、更安全。guard語句會檢查一到多個情況,找出不符合else部分的調用。而else部分需要return,break,continue或throw語句來終止方法的執行,也就是說終止程序控制的執行。

我們使用guard語句來減少代碼混亂,并避免在if/else語句中的嵌入。由于在guard語句的else部分中,代碼必須轉移程序控制的范圍,如果出現無效的情況,簡單地采用if語句來調用return語句更為安全。在編譯時這些bug仍有可能出現。如果guard語句的情況通過的話,在我們的范圍中,解包后的可選值仍舊可用。
class ProjectManager {    func increaseProductivityOfDeveloper(developer: Developer) {        guard let developerName = developer.name else {            print("Papers, please!")            return        }        let slackMessage = SlackMessage(message: "(developerName) is a great iOS Developer!")        slackMessage.send()    }}

用Defer管理程序控制流
defer語句會推遲包含這個命令的代碼執行,直到當前范圍終止。也就是說,在defer語句中清理邏輯是可以替換的,而且只要離開相應的調用范圍,這段命令就肯定就會被調用。這樣可以減少冗余步驟,更重要的是增加安全性。
func deferExample() {    defer {        print("Leaving scope, time to cleanup!")    }    print("Performing some operation...")}// Prints:// Performing some operation...// Leaving scope, time to cleanup!

簡化單例模式(Singleton)
在任何語言中對單例模式的使用都屬于熱議話題,不過它仍是大多數開發人員非常熟悉的模式。在Objective-C中,實現單例模式包括多個步驟,以便確保不會多次創建單例模式類。在Swift中這種使用有了大幅簡化。下面我們會看到在Objective-C中實現單例模式的代碼行數,是在Swift中實現單例模式代碼的兩倍。除此之外,由于使用了dispatch token,不僅可讀性較差,也很難記住。

Objective-C:
@implementation MySingletonClass    +(id)sharedInstance {        static MySingletonClass *sharedInstance = nil;        static dispatch_once_t onceToken;        dispatch_once(&onceToken, ^{            sharedInstance = [[self alloc] init];        });        return sharedInstance;    }

Swift:
class MySingletonClass {    static let sharedInstance = MySingletonClass()    private init() {    }}

通過協議擴展減少重復的代碼
在Objective-C中,我們通過分類來擴展已有的類型,不過這種做法對協議無效。Swift允許向協議中添加功能,使用Swift可以擴展單協議(甚至在標準數據庫中的那些!),并將其應用在實現協議的類中。協議擴展足夠將我們的整個編程范式從面向對象式改為面向協議式。這個概念的關鍵在于,我們默認通過協議來添加功能,而不是通過類,以便增加代碼的可復用性。想要了解更多面向協議編程的知識,請查看這個視頻,摘自WWDC 2015。

創建全局Helper函數
全局變量和函數經常被合稱為“壞東西”,不過事實是兩者都能讓代碼更干凈,真正的壞東西是全局狀態。全局函數經常需要全局狀態來完成相關工作,因此很容易理解它們為什么會有這樣的壞名聲。下面是一些Grand Central Dispatch的helper函數樣例,不是建立在全局狀態之上,而且多少有些語法糖(指計算機語言中添加的某種語法,這種語法對語言的功能并沒有影響,但是更方便程序員使用)的性質。下面我們會采用dispatch_after函數,用Swift的方式來解包:
import Foundation/**    Executes the closure on the main queue after a set amount of seconds.    - parameter delay:   Delay in seconds    - parameter closure: Code to execute after delay*/func delayOnMainQueue(delay: Double, closure: ()->()) {    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_main_queue(), closure)}/**    Executes the closure on a background queue after a set amount of seconds.    - parameter delay:   Delay in seconds    - parameter closure: Code to execute after delay*/func delayOnBackgroundQueue(delay: Double, closure: ()->()) {    dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_global_queue(QOS_CLASS_UTILITY, 0), closure)}

下面是新解包的函數樣例:
delayOnBackgroundQueue(5) {    showView()}

下面是未解包的函數樣例:
dispatch_after(dispatch_time(DISPATCH_TIME_NOW, Int64(delay * Double(NSEC_PER_SEC))), dispatch_get_global_queue(QOS_CLASS_UTILITY, 0)) {    showView()}

用Swift語法來解包C函數,讓我們的代碼更易于一眼理解。找到你最喜歡的函數,試一下吧!只要在正確方法命名上盡責,將來程序的維護者肯定感激我們。如果我們將上面的方法簽名修改為delay(delay: Double, closure: ()->()),這就成了不負責任的方法命名反例,因為dispatch_after需要GCD隊列,而從名稱中看不出來使用的哪個隊列。然而,如果我們使用的代碼庫在主線程所有方法的執行上有既定規范,除非在名稱或評論上另有指示,delay(delay: Double, closure: ()->())就可以是一個正確的方法名稱。無論我們如何命名helper函數,它們都是為了通過包裝樣本代碼節省時間,讓代碼更易讀。

擴展集合性能
Swift增加了一些方法,幫助我們對集合進行簡潔的查詢和修改。這些集合方法受到了函數式語言的啟發。我們使用集合將多個值保存到一個單獨的數據結構中,通常我們也會查詢和修改集合。這些函數是基于Swift的標準數據庫構建,協助簡化常見的任務。為了協助詮釋下面這些函數,我們使用了這些樣例:let ints = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]。

  • 對集合中的每個值執行閉包映射(map),之后返回填充有映射值的映射結果類型數組。下面我們將Int數組轉化為字符串數據:

let strings = ints.map { return String($0) }print("strings: (strings)") // prints: strings: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]

  • 對數組中的每個值執行函數篩選(filter),返回Bool值。在結果數組中,只會返回true值,而不會返回false值。下面我們從ints數組中篩選奇數:

let evenInts = ints.filter { return ($0 % 2 == 0) }print("evenInts: (evenInts)") // prints: evenInts: [0, 2, 4, 6, 8]

  • reduce比map和filter更復雜,不過因為非常有用,花時間學習也是有價值的。第一個參數就是第一個reduce值(在下面的案例中為0)。第二個參數是訪問之前reduce值和數組現值的函數。在本例中,我們的函數是將之前的函數值簡單加到數組的現值中。

let reducedInts = ints.reduce(0, combine: +)print("reducedInts: (reducedInts)") // prints: reducedInts: 45// defined another way: let reducedIntsAlt = ints.reduce(0) { (previousValue: Int, currentValue: Int) -> Int in    return previousValue + currentValue}print("reducedIntsAlt: (reducedIntsAlt)") // prints: reducedIntsAlt: 45

通過map,filter,reduce方面的技巧,就能減少篩選時和處理集合時的工作量,并增加可讀性,方便以后的人維護。

結論
這份列表來自于我們團隊的建議,收集了一些最常用的技巧,其中很多在整個代碼庫中都很常見。隨著Swift這門編程語言的發展,像這樣的技巧也在繼續增加。我們希望能繼續看到Swift的變化,并期待在應用中更多地使用這種語言。

英文來源:15 Tips to Become a Better Swift Developer
作者:NATHAN HILLYER,全棧開發者
翻譯:孫薇

相關熱詞搜索:Swift 移動開發 language 編程語言

上一篇:Swoole-1.8.2已發布,支持Http2.0協議
下一篇:使用Docker容器應該避免的10個事情

分享到: 收藏