歡迎來到Linux教程網
Linux教程網
Linux教程網
Linux教程網
Linux教程網 >> Linux編程 >> Linux編程 >> Swift編程的15個技巧

Swift編程的15個技巧

日期:2017/3/1 9:18:25   编辑:Linux編程

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

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

你應當了解,但有可能不知道的Swift技巧

提高常數的可讀性

在Swift中使用struct的簡潔辦法,就是在應用中制作一個適用所有常數的文件。由於Swift允許我們嵌用下面的結構,這種辦法非常有用:

import Foundation
struct 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 UIKit
class AwesomeClass {
    dynamic func originalFunction() -> String {
        return "originalFunction"
    }  
    dynamic func swizzledFunction() -> String {
        return "swizzledFunction"
    }
}
let awesomeObject = AwesomeClass()
print(awesomeObject.originalFunction()) // prints: "originalFunction"
let aClass = AwesomeClass.self
let 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 | message
printAlertWithMessage("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的變化,並期待在應用中更多地使用這種語言。

Ubuntu 15.10安裝部署Swift開發環境 http://www.linuxidc.com/Linux/2016-01/126995.htm

Swift 的變化:從 2.2 到 3.0 會帶來什麼 http://www.linuxidc.com/Linux/2015-12/126440.htm

Swift 正式開源,同時開源 Swfit 核心庫和包管理器 http://www.linuxidc.com/Linux/2015-12/125847.htm

Apple Swift學習教程 http://www.linuxidc.com/Linux/2014-09/106420.htm

使用 Swift 構建一個 iOS 的郵件應用 http://www.linuxidc.com/Linux/2014-08/105542.htm

Swift 2.0開源化 http://www.linuxidc.com/Linux/2015-06/118594.htm

Linux下搭建Swift語言開發學習環境 http://www.linuxidc.com/Linux/2015-12/125983.htm

Swift 的詳細介紹:請點這裡

英文來源: 15 Tips to Become a Better Swift Developer

Copyright © Linux教程網 All Rights Reserved